Samples
CTRL   IS   WD 
1303 1033 1181 
# The [[ operator can add columns to object metadata. This is a great place to stash QC stats
oligos[["percent.mt"]] <- PercentageFeatureSet(oligos, pattern = "^mt-")
# Visualize QC metrics as a violin plot
VlnPlot(oligos, group.by = "Sample",features = c("nFeature_RNA", "nCount_RNA", "percent.mt"), ncol = 1,pt.size = 0.1)

# FeatureScatter is typically used to visualize feature-feature relationships, but can be used
# for anything calculated by the object, i.e. columns in object metadata, PC scores etc.
plot1 <- FeatureScatter(oligos, group.by = "Sample",feature1 = "nCount_RNA", feature2 = "percent.mt",pt.size = 0.5)
plot2 <- FeatureScatter(oligos, group.by = "Sample", feature1 = "nCount_RNA", feature2 = "nFeature_RNA",pt.size = 0.5)
CombinePlots(plots = list(plot1, plot2))
CombinePlots is being deprecated. Plots should now be combined using the patchwork system.

#Clean up the data
oligos <- subset(oligos, subset = nFeature_RNA > 500 & nFeature_RNA < 7000 & percent.mt < 10)
ncol(oligos)
[1] 1975
# The [[ operator can add columns to object metadata. This is a great place to stash QC stats
oligos[["percent.mt"]] <- PercentageFeatureSet(oligos, pattern = "^mt-")
# Visualize QC metrics as a violin plot
VlnPlot(oligos, group.by = "Sample",features = c("nFeature_RNA", "nCount_RNA", "percent.mt"), ncol = 1,pt.size = 0.1)

# FeatureScatter is typically used to visualize feature-feature relationships, but can be used
# for anything calculated by the object, i.e. columns in object metadata, PC scores etc.
plot1 <- FeatureScatter(oligos, group.by = "Sample",feature1 = "nCount_RNA", feature2 = "percent.mt",pt.size = 0.5)
plot2 <- FeatureScatter(oligos, group.by = "Sample", feature1 = "nCount_RNA", feature2 = "nFeature_RNA",pt.size = 0.5)
CombinePlots(plots = list(plot1, plot2))
CombinePlots is being deprecated. Plots should now be combined using the patchwork system.

Now we normalize the dataset.

Generating the UMAP and TSNE.

#DefaultAssay(oligos.integrated) <- "integrated"
oligos.integrated <- RunPCA(oligos.integrated, verbose = FALSE)
oligos.integrated <- RunUMAP(oligos.integrated, dims = 1:30)
The default method for RunUMAP has changed from calling Python UMAP via reticulate to the R-native UWOT using the cosine metric
To use Python UMAP via reticulate, set umap.method to 'umap-learn' and metric to 'correlation'
This message will be shown once per session16:37:23 UMAP embedding parameters a = 0.9922 b = 1.112
16:37:23 Read 1975 rows and found 30 numeric columns
16:37:23 Using Annoy for neighbor search, n_neighbors = 30
16:37:23 Building Annoy index with metric = cosine, n_trees = 50
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
16:37:24 Writing NN index file to temp file /var/folders/98/j14xl9ln5190l9fvq32g5rkw0000gn/T//RtmpSdWnAZ/file3f2e39773684
16:37:24 Searching Annoy index using 1 thread, search_k = 3000
16:37:24 Annoy recall = 100%
16:37:25 Commencing smooth kNN distance calibration using 1 thread
16:37:25 Initializing from normalized Laplacian + noise
16:37:28 Commencing optimization for 500 epochs, with 77718 positive edges
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
16:37:31 Optimization finished
#oligos.integrated <- RunTSNE(oligos.integrated, dims = 1:30)
plots <- DimPlot(oligos.integrated, group.by = c("Sample"), combine = FALSE)
plots <- lapply(X = plots, FUN = function(x) x + theme(legend.position = "top") + guides(color = guide_legend(nrow = 3, 
    byrow = TRUE, override.aes = list(size = 3))))
CombinePlots(plots)
CombinePlots is being deprecated. Plots should now be combined using the patchwork system.

# plots <- TSNEPlot(oligos.integrated, group.by = c("Sample"), combine = FALSE)
# plots <- lapply(X = plots, FUN = function(x) x + theme(legend.position = "top") + guides(color = guide_legend(nrow = 3, 
#     byrow = TRUE, override.aes = list(size = 3))))
# CombinePlots(plots)

Label transfer

Now we attempt to transfer the cluster labels of the Science dataset onto the 10X dataset.

oligos.integrated <- FindNeighbors(oligos.integrated, dims = 1:30)
Computing nearest neighbor graph
Computing SNN
oligos.integrated <- FindClusters(oligos.integrated,algorithm = 4,resolution = 0.6)
#0.6
oligos.integrated$predicted.id <- factor(oligos.integrated$predicted.id,levels=c("OPC","COP","NFOL1","MFOL1","MFOL2","MOL1","MOL2","MOL3","MOL4","MOL5","MOL6","PPR"))
DimPlot(oligos.integrated, group.by = c("seurat_clusters"), combine = FALSE)
[[1]]

DimPlot(oligos.integrated, group.by = c("predicted.id"), combine = FALSE)
[[1]]

DimPlot(oligos.integrated, group.by = c("Sample"), combine = FALSE)
[[1]]

table(oligos.integrated$Sample,oligos.integrated$predicted.id)
      
       OPC COP NFOL1 MFOL1 MFOL2 MOL1 MOL2 MOL3 MOL4 MOL5 MOL6 PPR
  CTRL  37   5     2     6     2   77  153    8   15  156  264   1
  IS     8   1     5     1     1  272   80    6   15  180   62  12
  WD    33   5     3     2     0   26  122    5   10  118  269   4
#subset OPCs
oligos.integratedIm <- subset(oligos.integrated,seurat_clusters %in% c(12,11,8,9))
oligos.integratedIm <- FindVariableFeatures(oligos.integratedIm)
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
DefaultAssay(oligos.integratedIm) <- "SCT"

#Spatially filter genes with auto bootstrapping #Script will give error when running out of genes, this is normal

networkExpressionFile=oligos.integratedIm@assays$SCT@scale.data
expr_limit <- min(networkExpressionFile)-0.01
featureselection <- row.names(networkExpressionFile)
gc()
            used   (Mb) gc trigger   (Mb)  max used   (Mb)
Ncells   2854600  152.5    5203605  278.0   3953528  211.2
Vcells 350219390 2672.0  648326055 4946.4 645570740 4925.4
library(umap)
useUMAP <- "TRUE"
UMAPdim <- 3
adjustforbias <- "FALSE"
GeneMarkovList <- list()
GeneFilterProgression <- as.data.frame(featureselection)
row.names(GeneFilterProgression) <- featureselection
corenumber <- 4
usemagic <- "FALSE"
pcathresh <- 20
ncompGF <- 20
whentobootstrap <- 20000 #cellnumber
howmanyboots <- 10+1 #bootstrap repetitions
samplesize <- 2000 #how many cells to take for sampling
threshold <- 3 #use mean spatial correlation of geneset of the featureselection (1) or after the first round of filtering (2) (more strict, which is default) 
nsim <- 100
nnquant <- 0.995 #lower for small datasets, higher for speedups in larger datasets
set.seed(1234)
tri.to.squ<-function(x)
{
rn<-row.names(x)
cn<-colnames(x)
an<-unique(c(cn,rn))
myval<-x[!is.na(x)]
mymat<-matrix(1,nrow=length(an),ncol=length(an),dimnames=list(an,an))
for(ext in 1:length(cn))
{
 for(int in 1:length(rn))
 {
 if(is.na(x[row.names(x)==rn[int],colnames(x)==cn[ext]])) next
 mymat[row.names(mymat)==rn[int],colnames(mymat)==cn[ext]]<-x[row.names(x)==rn[int],colnames(x)==cn[ext]]
 mymat[row.names(mymat)==cn[ext],colnames(mymat)==rn[int]]<-x[row.names(x)==rn[int],colnames(x)==cn[ext]]
 }
}
return(mymat)
}
SCN3Egeneset <- featureselection
originalnetworkdf <- networkExpressionFile
if(ncol(networkExpressionFile)>whentobootstrap){
originalnetworkdf <- networkExpressionFile
bootstrap<-1
sample<-1
moransIsampled <- as.character(NULL)
}
if(ncol(networkExpressionFile)<=whentobootstrap)
{
  bootstrap<-howmanyboots-1
  sample<-0
  }
while(bootstrap<howmanyboots){
SCN3Egeneset <- featureselection
iter=0
meanMoran<-0
if(sample==1){
networkExpressionFile  <- originalnetworkdf[,sample(ncol(originalnetworkdf), samplesize) ]
print(paste("bootstrapping...","round",bootstrap))
}
OldGeneset <- as.character(seq_len(100000))
while(length(OldGeneset) > (length(SCN3Egeneset)+10) ){ # uncomment this and below to enable iterative filtering
 iter=iter+1
  #library(amap)
#library(MASS)
#library(destiny)
#library(dpt)
#library(Matrix)
#library(diffusionMap)
print(paste("Preparing genefiltering on", length(SCN3Egeneset), "genes."))
#print("Making celllandscape...Filtering possible duplicated cells in original file")
#if(length(OldGeneset)==100000)
#{
#  pca <- prcomp(t(log(networkExpressionFile[featureselection,]+1)),scale. = FALSE)
#  cd_diffusionplot <-pca$x[,c(1:30)]
#  }
#if(length(OldGeneset)!=100000){cd_diffusionplot <- t(log(networkExpressionFile[SCN3Egeneset,]+1))}
  if(length(SCN3Egeneset) > pcathresh){
#library(rsvd)
    print("Making celllandscape...Performing dimensionality reduction")
#pca <- rpca(t(log(networkExpressionFile[SCN3Egeneset,]+1)))
#pca <- prcomp(t(log(networkExpressionFile[SCN3Egeneset,]+1)),scale. = TRUE)
pca <- prcomp(t(networkExpressionFile[SCN3Egeneset,]),scale. = TRUE)
if(length(SCN3Egeneset) < 100) {ncompGF <- length(SCN3Egeneset)}
cd_diffusionplot <-pca$x[,c(1:which(cumsum(pca$sdev[1:ncompGF])/sum(pca$sdev[1:ncompGF]) > 0.8)[1])]
  }
   if(length(SCN3Egeneset) <= pcathresh){
#cd_diffusionplot <-t(log(networkExpressionFile[SCN3Egeneset,]+1))
cd_diffusionplot <-t(networkExpressionFile[SCN3Egeneset,])
}
cd_diffusionplot <- cd_diffusionplot[!duplicated(cd_diffusionplot),]
print("Making celllandscape...Making diffusionmap")
# gc()
#ts <- Transitions(cd_diffusionplot,k=20)
if(useUMAP==TRUE)
{
  library(umap)
umap.settings <- umap.defaults
umap.settings$n_neighbors <-10
umap.settings$min_dist <- 0
umap.settings$n_components <- UMAPdim
umap.settings$metric <- "manhattan"
#umap.settings$local_connectivity <- 0.00001
umap.settings$n_epochs <- 1000
umap_out <- umap(cd_diffusionplot,config = umap.settings,method="umap-learn")
}
if(useUMAP != TRUE){
library(destiny)
ts <- DiffusionMap(cd_diffusionplot, verbose = TRUE)
}
if(adjustforbias==TRUE){
testwilcox <- as.matrix(apply(t(ts@eigenvectors),1,function(x) apply(tri.to.squ(pairwise.wilcox.test(x,biasedfactor,p.adjust.method = "fdr")$p.value),2,function(x) min(x,na.rm=TRUE))))
testwilcoxdifference <- scale(t(testwilcox))
biasedcomponents <- unique(unlist(apply(testwilcoxdifference,2,function(x) which(abs(x) > 1.96))))
}
#Gene filtering
print(paste("Filtering", length(SCN3Egeneset), "genes."))
OldGeneset <- SCN3Egeneset
#NetworkDist <- as.matrix(ts@transitions)
library(amap)
range01 <- function(x){(x-min(x))/(max(x)-min(x))}
if(useUMAP==TRUE)
  {
  if(adjustforbias==TRUE){
NetworkDist <- 1-range01((as.matrix(Dist(umap_out$layout[,1:UMAPdim],method = "manhattan",nbproc = 8))))}
else{NetworkDist <- 1-range01((as.matrix(Dist(umap_out$layout[,1:UMAPdim],method = "manhattan",nbproc = 8))))}
}
if(useUMAP != TRUE){
if(adjustforbias==TRUE){
NetworkDist <- 1-range01((as.matrix(Dist(ts@eigenvectors[,-c(biasedcomponents)],method = "manhattan",nbproc = 8))))}
else{NetworkDist <- 1-range01((as.matrix(Dist(ts@eigenvectors,method = "manhattan",nbproc = 8))))}
}
#NetworkDist <- distcells*distcells2
colnames(NetworkDist) <- row.names(cd_diffusionplot)
row.names(NetworkDist) <- row.names(cd_diffusionplot)
emat_expressed <- apply(networkExpressionFile,1,function(x) any ((x) >expr_limit))
emat_expressed <- networkExpressionFile[emat_expressed,row.names(NetworkDist)]
emat_expressed <- as.matrix(emat_expressed[intersect(row.names(emat_expressed),OldGeneset),])
if(usemagic=="TRUE"){
emat_expressed <- magic(ts,emat_expressed[,row.names(cd_diffusionplot)], power = 1, k = 20, n_eigs = 20, n_local = 10)
}
#library(doParallel)
#library(foreach)
library(parallel)
library(spdep)
cores <- corenumber
# cl <- makeCluster(cores)  
# registerDoParallel(cl)  
SCN3Egenefilter <- matrix(nrow=nrow(emat_expressed),ncol = 3) 
cellnames <- colnames(emat_expressed)
# library(Matrix)
# NetworkDistsparse <- Matrix(NetworkDist, sparse = TRUE)
print("Making celllandscape...Generating Spatial Weights Matrix...")
i=1
NetworkDist2 <- matrix(nrow=nrow(NetworkDist),ncol=ncol(NetworkDist))
  for(i in 1:ncol(NetworkDist2))
    {
  x <-NetworkDist[,i] 
 x[x < as.numeric(quantile(x,nnquant))] <- 0
 NetworkDist2[,i] <- x
  }
NetworkDist <- NetworkDist2
rm(NetworkDist2)
colnames(NetworkDist) <- row.names(cd_diffusionplot)
row.names(NetworkDist) <- row.names(cd_diffusionplot)
spatial.weights <- mat2listw(NetworkDist[])
print("Making celllandscape...Generating Spatial Weights Matrix...done")
numbsim <- nsim
i=1
test <- as.data.frame(mclapply(1:nrow(emat_expressed), function(i) {
   return(m <- c(unlist(moran.mc(emat_expressed[i,],spatial.weights,nsim=numbsim,zero.policy = TRUE))[1:3],row.names(emat_expressed)[i]))
}, mc.cores=corenumber))
SCN3Egenefilter <- t(test)
SCN3Egenefilter <- SCN3Egenefilter[! apply(SCN3Egenefilter,1,function(x) any(x=="NaN")),]
row.names(SCN3Egenefilter) <- SCN3Egenefilter[,4]
SCN3Egenefilter_clean <- as.data.frame(SCN3Egenefilter[complete.cases(SCN3Egenefilter),c(1:4)])
 r <- row.names(SCN3Egenefilter_clean)
SCN3Egenefilter_clean <- apply(SCN3Egenefilter_clean,2,function(x) as.numeric(x))
row.names(SCN3Egenefilter_clean) <- r
SCN3Egeneset <- row.names(subset(SCN3Egenefilter_clean,SCN3Egenefilter_clean[,3] <= 0.01))
HSEgenes <- row.names(subset(SCN3Egenefilter_clean,SCN3Egenefilter_clean[,3] <= 0.01 & SCN3Egenefilter_clean[,1] >= as.numeric(quantile(SCN3Egenefilter_clean[SCN3Egeneset,1],0.1))))
SCN3Egeneset <- row.names(subset(SCN3Egenefilter_clean,SCN3Egenefilter_clean[,3] <= 0.01 & SCN3Egenefilter_clean[,1] >= as.numeric(quantile(SCN3Egenefilter_clean[SCN3Egeneset,1],0.1))))
GeneFilterProgression <- cbind(GeneFilterProgression[SCN3Egeneset,],SCN3Egenefilter_clean[SCN3Egeneset,1])
if(iter<=threshold){meanMoran<-mean(abs(SCN3Egenefilter_clean[,1]),na.rm=TRUE)}
GeneMarkovList <- c(list(GeneMarkov=SCN3Egeneset),GeneMarkovList)
} #uncomment this to enable iterative filtering
bootstrap <- bootstrap+1
if(sample==1){
moransIsampled <- c(moransIsampled,SCN3Egeneset)
}
}
[1] "Preparing genefiltering on 3000 genes."
[1] "Making celllandscape...Performing dimensionality reduction"
[1] "Making celllandscape...Making diffusionmap"
[1] "Filtering 3000 genes."
[1] "Making celllandscape...Generating Spatial Weights Matrix..."
[1] "Making celllandscape...Generating Spatial Weights Matrix...done"
NAs introduced by coercion
[1] "Preparing genefiltering on 1835 genes."
[1] "Making celllandscape...Performing dimensionality reduction"
[1] "Making celllandscape...Making diffusionmap"
[1] "Filtering 1835 genes."
[1] "Making celllandscape...Generating Spatial Weights Matrix..."
[1] "Making celllandscape...Generating Spatial Weights Matrix...done"
NAs introduced by coercion
[1] "Preparing genefiltering on 1553 genes."
[1] "Making celllandscape...Performing dimensionality reduction"
[1] "Making celllandscape...Making diffusionmap"
[1] "Filtering 1553 genes."
[1] "Making celllandscape...Generating Spatial Weights Matrix..."
[1] "Making celllandscape...Generating Spatial Weights Matrix...done"
NAs introduced by coercion
[1] "Preparing genefiltering on 1375 genes."
[1] "Making celllandscape...Performing dimensionality reduction"
[1] "Making celllandscape...Making diffusionmap"
[1] "Filtering 1375 genes."
[1] "Making celllandscape...Generating Spatial Weights Matrix..."
[1] "Making celllandscape...Generating Spatial Weights Matrix...done"
NAs introduced by coercion
[1] "Preparing genefiltering on 1229 genes."
[1] "Making celllandscape...Performing dimensionality reduction"
[1] "Making celllandscape...Making diffusionmap"
[1] "Filtering 1229 genes."
[1] "Making celllandscape...Generating Spatial Weights Matrix..."
[1] "Making celllandscape...Generating Spatial Weights Matrix...done"
NAs introduced by coercion
[1] "Preparing genefiltering on 1101 genes."
[1] "Making celllandscape...Performing dimensionality reduction"
[1] "Making celllandscape...Making diffusionmap"
[1] "Filtering 1101 genes."
[1] "Making celllandscape...Generating Spatial Weights Matrix..."
[1] "Making celllandscape...Generating Spatial Weights Matrix...done"
NAs introduced by coercion
[1] "Preparing genefiltering on 991 genes."
[1] "Making celllandscape...Performing dimensionality reduction"
[1] "Making celllandscape...Making diffusionmap"
[1] "Filtering 991 genes."
[1] "Making celllandscape...Generating Spatial Weights Matrix..."
[1] "Making celllandscape...Generating Spatial Weights Matrix...done"
NAs introduced by coercion
[1] "Preparing genefiltering on 880 genes."
[1] "Making celllandscape...Performing dimensionality reduction"
[1] "Making celllandscape...Making diffusionmap"
[1] "Filtering 880 genes."
[1] "Making celllandscape...Generating Spatial Weights Matrix..."
[1] "Making celllandscape...Generating Spatial Weights Matrix...done"
NAs introduced by coercion
[1] "Preparing genefiltering on 792 genes."
[1] "Making celllandscape...Performing dimensionality reduction"
[1] "Making celllandscape...Making diffusionmap"
[1] "Filtering 792 genes."
[1] "Making celllandscape...Generating Spatial Weights Matrix..."
[1] "Making celllandscape...Generating Spatial Weights Matrix...done"
NAs introduced by coercion
[1] "Preparing genefiltering on 709 genes."
[1] "Making celllandscape...Performing dimensionality reduction"
[1] "Making celllandscape...Making diffusionmap"
[1] "Filtering 709 genes."
[1] "Making celllandscape...Generating Spatial Weights Matrix..."
[1] "Making celllandscape...Generating Spatial Weights Matrix...done"
NAs introduced by coercion
[1] "Preparing genefiltering on 638 genes."
[1] "Making celllandscape...Performing dimensionality reduction"
[1] "Making celllandscape...Making diffusionmap"
[1] "Filtering 638 genes."
[1] "Making celllandscape...Generating Spatial Weights Matrix..."
[1] "Making celllandscape...Generating Spatial Weights Matrix...done"
NAs introduced by coercion
[1] "Preparing genefiltering on 574 genes."
[1] "Making celllandscape...Performing dimensionality reduction"
[1] "Making celllandscape...Making diffusionmap"
[1] "Filtering 574 genes."
[1] "Making celllandscape...Generating Spatial Weights Matrix..."
[1] "Making celllandscape...Generating Spatial Weights Matrix...done"
NAs introduced by coercion
[1] "Preparing genefiltering on 515 genes."
[1] "Making celllandscape...Performing dimensionality reduction"
[1] "Making celllandscape...Making diffusionmap"
[1] "Filtering 515 genes."
[1] "Making celllandscape...Generating Spatial Weights Matrix..."
[1] "Making celllandscape...Generating Spatial Weights Matrix...done"
NAs introduced by coercion
[1] "Preparing genefiltering on 463 genes."
[1] "Making celllandscape...Performing dimensionality reduction"
[1] "Making celllandscape...Making diffusionmap"
[1] "Filtering 463 genes."
[1] "Making celllandscape...Generating Spatial Weights Matrix..."
[1] "Making celllandscape...Generating Spatial Weights Matrix...done"
NAs introduced by coercion
[1] "Preparing genefiltering on 416 genes."
[1] "Making celllandscape...Performing dimensionality reduction"
[1] "Making celllandscape...Making diffusionmap"
[1] "Filtering 416 genes."
[1] "Making celllandscape...Generating Spatial Weights Matrix..."
[1] "Making celllandscape...Generating Spatial Weights Matrix...done"
NAs introduced by coercion
[1] "Preparing genefiltering on 374 genes."
[1] "Making celllandscape...Performing dimensionality reduction"
[1] "Making celllandscape...Making diffusionmap"
[1] "Filtering 374 genes."
[1] "Making celllandscape...Generating Spatial Weights Matrix..."
[1] "Making celllandscape...Generating Spatial Weights Matrix...done"
NAs introduced by coercion
[1] "Preparing genefiltering on 336 genes."
[1] "Making celllandscape...Performing dimensionality reduction"
[1] "Making celllandscape...Making diffusionmap"
[1] "Filtering 336 genes."
[1] "Making celllandscape...Generating Spatial Weights Matrix..."
[1] "Making celllandscape...Generating Spatial Weights Matrix...done"
NAs introduced by coercion
[1] "Preparing genefiltering on 302 genes."
[1] "Making celllandscape...Performing dimensionality reduction"
[1] "Making celllandscape...Making diffusionmap"
[1] "Filtering 302 genes."
[1] "Making celllandscape...Generating Spatial Weights Matrix..."
[1] "Making celllandscape...Generating Spatial Weights Matrix...done"
NAs introduced by coercion
[1] "Preparing genefiltering on 271 genes."
[1] "Making celllandscape...Performing dimensionality reduction"
[1] "Making celllandscape...Making diffusionmap"
[1] "Filtering 271 genes."
[1] "Making celllandscape...Generating Spatial Weights Matrix..."
[1] "Making celllandscape...Generating Spatial Weights Matrix...done"
NAs introduced by coercion
[1] "Preparing genefiltering on 244 genes."
[1] "Making celllandscape...Performing dimensionality reduction"
[1] "Making celllandscape...Making diffusionmap"
[1] "Filtering 244 genes."
[1] "Making celllandscape...Generating Spatial Weights Matrix..."
[1] "Making celllandscape...Generating Spatial Weights Matrix...done"
NAs introduced by coercion
[1] "Preparing genefiltering on 219 genes."
[1] "Making celllandscape...Performing dimensionality reduction"
[1] "Making celllandscape...Making diffusionmap"
[1] "Filtering 219 genes."
[1] "Making celllandscape...Generating Spatial Weights Matrix..."
[1] "Making celllandscape...Generating Spatial Weights Matrix...done"
NAs introduced by coercion
[1] "Preparing genefiltering on 197 genes."
[1] "Making celllandscape...Performing dimensionality reduction"
[1] "Making celllandscape...Making diffusionmap"
[1] "Filtering 197 genes."
[1] "Making celllandscape...Generating Spatial Weights Matrix..."
[1] "Making celllandscape...Generating Spatial Weights Matrix...done"
NAs introduced by coercion
[1] "Preparing genefiltering on 177 genes."
[1] "Making celllandscape...Performing dimensionality reduction"
[1] "Making celllandscape...Making diffusionmap"
[1] "Filtering 177 genes."
[1] "Making celllandscape...Generating Spatial Weights Matrix..."
[1] "Making celllandscape...Generating Spatial Weights Matrix...done"
NAs introduced by coercion
[1] "Preparing genefiltering on 159 genes."
[1] "Making celllandscape...Performing dimensionality reduction"
[1] "Making celllandscape...Making diffusionmap"
[1] "Filtering 159 genes."
[1] "Making celllandscape...Generating Spatial Weights Matrix..."
[1] "Making celllandscape...Generating Spatial Weights Matrix...done"
NAs introduced by coercion
[1] "Preparing genefiltering on 143 genes."
[1] "Making celllandscape...Performing dimensionality reduction"
[1] "Making celllandscape...Making diffusionmap"
[1] "Filtering 143 genes."
[1] "Making celllandscape...Generating Spatial Weights Matrix..."
[1] "Making celllandscape...Generating Spatial Weights Matrix...done"
NAs introduced by coercion
[1] "Preparing genefiltering on 128 genes."
[1] "Making celllandscape...Performing dimensionality reduction"
[1] "Making celllandscape...Making diffusionmap"
[1] "Filtering 128 genes."
[1] "Making celllandscape...Generating Spatial Weights Matrix..."
[1] "Making celllandscape...Generating Spatial Weights Matrix...done"
NAs introduced by coercion
[1] "Preparing genefiltering on 115 genes."
[1] "Making celllandscape...Performing dimensionality reduction"
[1] "Making celllandscape...Making diffusionmap"
[1] "Filtering 115 genes."
[1] "Making celllandscape...Generating Spatial Weights Matrix..."
[1] "Making celllandscape...Generating Spatial Weights Matrix...done"
NAs introduced by coercion
[1] "Preparing genefiltering on 0 genes."
[1] "Making celllandscape...Making diffusionmap"
Error in d[, 1] : subscript out of bounds
length_iterations <-  unlist(lapply(GeneMarkovList,function(x) length(x)))
plot(seq_along(length_iterations),length_iterations)

pcatsne <- prcomp(t(as.matrix(networkExpressionFile[unlist(GeneMarkovList[26]),])),scale. = TRUE)
pca <- pcatsne
plot(log(pcatsne$sdev[1:50]),pch = 20,
xlab = 'Principal component', ylab = 'sdev')

ncompL1tsne <- which(cumsum(pca$sdev[1:50])/sum(pca$sdev[1:50]) > 0.6)[1]
tsne <- t(pca$x[,c(1:ncompL1tsne)]) 
number_of_clusters <- 8
NetworkDist <- Dist(t(tsne),method = "manhattan",nbproc = 8)
GeneMarkov_hc <- hclust(NetworkDist, method = "ward.D2")
GMcluster <- rbind(groups = cutree(GeneMarkov_hc, k=number_of_clusters))
names(GMcluster) <- colnames(networkExpressionFile)
oligos.integratedIm$seurat_clusters <- as.factor(GMcluster)
GeneMarkov  <- unlist(GeneMarkovList[27])
DefaultAssay(oligos.integratedIm) <- "SCT"
oligos.integratedIm <- RunPCA(oligos.integratedIm, verbose = FALSE,features = GeneMarkov)#npcs = 20)
ElbowPlot(oligos.integratedIm)

oligos.integratedIm <- RunUMAP(oligos.integratedIm, dims = 1:10)
16:50:59 UMAP embedding parameters a = 0.9922 b = 1.112
16:50:59 Read 203 rows and found 10 numeric columns
16:50:59 Using Annoy for neighbor search, n_neighbors = 30
16:50:59 Building Annoy index with metric = cosine, n_trees = 50
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
16:51:00 Writing NN index file to temp file /var/folders/98/j14xl9ln5190l9fvq32g5rkw0000gn/T//RtmpSdWnAZ/file3f2e7275c4d0
16:51:00 Searching Annoy index using 1 thread, search_k = 3000
16:51:00 Annoy recall = 100%
16:51:00 Commencing smooth kNN distance calibration using 1 thread
16:51:01 Initializing from normalized Laplacian + noise
16:51:01 Commencing optimization for 500 epochs, with 6014 positive edges
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
16:51:01 Optimization finished
#oligos.integratedScience <- RunTSNE(oligos.integratedScience, dims = 1:10)
plots <- DimPlot(oligos.integratedIm, group.by = c("seurat_clusters"), combine = FALSE)
plots <- lapply(X = plots, FUN = function(x) x + theme(legend.position = "top") + guides(color = guide_legend(nrow = 3, 
    byrow = TRUE, override.aes = list(size = 3))))
CombinePlots(plots)
CombinePlots is being deprecated. Plots should now be combined using the patchwork system.

plots <- DimPlot(oligos.integratedIm, group.by = c("Sample"), combine = FALSE)
plots <- lapply(X = plots, FUN = function(x) x + theme(legend.position = "top") + guides(color = guide_legend(nrow = 3, 
    byrow = TRUE, override.aes = list(size = 3))))
CombinePlots(plots)
CombinePlots is being deprecated. Plots should now be combined using the patchwork system.

oligos.integrated.markersIm %>% group_by(cluster) %>% top_n(n = 2, wt = avg_logFC)
library(viridis)
DefaultAssay(oligos.integratedIm) <- "SCT"
top10 <- oligos.integrated.markersIm %>% group_by(cluster) %>% top_n(n = 10, wt = avg_logFC)
# DoHeatmap(oligos.integratedIm, features = intersect(oligos.integrated.markersIm$gene,GeneMarkov),disp.max=7,) + NoLegend() +scale_fill_viridis()
DoHeatmap(oligos.integratedIm, features =intersect(oligos.integrated.markersIm$gene,unique(c(unlist(GeneMarkovList[5]),top10$gene))),disp.max=3) + NoLegend() +scale_fill_viridis()
The following features were omitted as they were not found in the scale.data slot for the SCT assay: Cldn11, Mag, Mog, Mobp, ErmnScale for 'fill' is already present. Adding another scale for 'fill', which will replace the existing
scale.

nonOL <- colnames(oligos.integratedIm)[oligos.integratedIm@meta.data$seurat_clusters %in% c(3:6,8)]
oligos.integrated <- oligos.integrated[,! colnames(oligos.integrated) %in% nonOL]
data <- as.data.frame(table(oligos.integrated$Sample,droplevels(as.factor(oligos.integrated$predicted.id))))
colnames(data) <- c("Condition","Cluster","Freq")
library(plyr)
data$Cluster  <- factor(data$Cluster,levels=c("OPC","COP","NFOL1","MFOL1","MFOL2","MOL1","MOL2","MOL3","MOL4","MOL5","MOL6","PPR"))
data <- data[which(data$Cluster %in% c("MFOL1","MFOL2","MOL1","MOL2","MOL3","MOL4","MOL5","MOL6")),]
#data$Cluster  <- factor(data$Cluster,levels=c("MFOL1","MFOL2","MOL1","MOL2","MOL3","MOL4","MOL5","MOL6"))
library(reshape2)
datacasted <- dcast(data,Cluster ~ Condition)
Using Freq as value column: use value.var to override.
calc_cpm <-function (expr_mat) 
{
    norm_factor <- colSums(expr_mat)
    return(t(t(expr_mat)/norm_factor))
}
datacasted[,2:4] <- calc_cpm(datacasted[,2:4])
data <- melt(datacasted)
Using Cluster as id variables
colnames(data) <- c("Condition","Cluster","Freq")
#data$Cluster  <- revalue(as.factor(data$Cluster),c("PPR"="VLMC"))
# Stacked + percent
ggplot(data, aes(fill=Condition, y=Freq, x=Cluster)) + 
    geom_bar(position="fill", stat="identity")

ggplot(data, aes(fill=Cluster, y=Freq, x=Condition)) + 
    geom_bar( stat="identity")

row.names(datacasted) <- datacasted[,1]
datacasted <- datacasted[,2:4]*100
datamelted <- melt(t(datacasted))
ggplot(datamelted, aes(y = value, x = Var2)) + # Move y and x here so than they can be used in stat_*
    geom_dotplot(aes(fill = Var1),   # Use fill = Species here not in ggplot()
                 binaxis = "y",         # which axis to bin along
                 binwidth = 1,        # Minimal difference considered diffeerent
                 stackdir = "center",
                 position = position_jitter(0.2)# Centered
                 ) +  # scale_y_log10() + 
    stat_summary(fun.y = mean, fun.ymin = mean, fun.ymax = mean,
                 geom = "crossbar", width = 0.5,fatten = 0.01) + theme(axis.text.x = element_text(angle = 45))

library(heatmap3)
library(viridis)
data <- as.data.frame(table(oligos.integrated$Sample,droplevels(as.factor(oligos.integrated$predicted.id))))
colnames(data) <- c("Condition","Cluster","Freq")
library(plyr)
data$Cluster  <- factor(data$Cluster,levels=c("OPC","COP","NFOL1","MFOL1","MFOL2","MOL1","MOL2","MOL3","MOL4","MOL5","MOL6","PPR"))
data <- data[which(data$Cluster %in% c("MFOL1","MFOL2","MOL1","MOL2","MOL3","MOL4","MOL5","MOL6")),]
#data$Cluster  <- factor(data$Cluster,levels=c("MFOL1","MFOL2","MOL1","MOL2","MOL3","MOL4","MOL5","MOL6"))
library(reshape2)
datacasted <- dcast(data,Cluster ~ Condition)
Using Freq as value column: use value.var to override.
calc_cpm <-function (expr_mat) 
{
    norm_factor <- colSums(expr_mat)
    return(t(t(expr_mat)/norm_factor))
}
datacasted[,2:4] <- calc_cpm(datacasted[,2:4])*100
row.names(datacasted) <- datacasted[,1]
datacasted <- datacasted[,2:4]
comparison <-datacasted-apply(datacasted,1,function(x) mean(x))
comparison <-datacasted-datacasted[,1]
heatmap3(comparison[rev(row.names(comparison)),], Rowv = NA , Colv = NA ,scale = "none",symm = F, method = "ward.D2",col=viridis(1000),balanceColor =F,cexRow = 1,cexCol = 1,margins = c(10, 10))

library(RColorBrewer)
heatmap3(comparison[rev(row.names(comparison)),], Rowv = NA , Colv = NA ,scale = "none",symm = F, method = "ward.D2",col=rev(colorRampPalette(brewer.pal(1024,"RdBu"))(1024)),balanceColor =T,cexRow = 1,cexCol = 1,margins = c(10, 10))
n too large, allowed maximum for palette RdBu is 11
Returning the palette you asked for with that many colors

  relationshipratio <- cor(t(comparison),method="pearson")
heatmap3(relationshipratio[rev(row.names(comparison)),], Rowv = NULL , Colv = NULL ,scale = "none",symm = F, method = "ward.D2",col=colorRampPalette(c("limegreen","black",
"firebrick3"))(1024),balanceColor =F,cexRow = 2,cexCol = 2,margins = c(10, 10))

barplot(table(oligos.integrated$Sample,oligos.integrated$predicted.id))

data <- as.data.frame(table(oligos.integrated$Sample,oligos.integrated$predicted.id))
colnames(data) <- c("Condition","Cluster","Freq")
library(plyr)
data$Cluster  <- factor(data$Cluster,levels=c("OPC","COP","NFOL1","MFOL1","MFOL2","MOL1","MOL2","MOL3","MOL4","MOL5","MOL6","PPR"))
data$Cluster  <- revalue(as.factor(data$Cluster),c("PPR"="VLMC"))
dataIS_CTRL <- data[which(data$Condition %in% c("IS","CTRL")),]
dataWD_CTRL <- data[which(data$Condition %in% c("WD","CTRL")),]
dataIS_WD <- data[which(data$Condition %in% c("IS","WD")),]
# Stacked + percent
ggplot(data, aes(fill=Condition, y=Freq, x=Cluster)) + 
    geom_bar(position="fill", stat="identity")

ggplot(dataIS_CTRL, aes(fill=Condition, y=Freq, x=Cluster)) + 
    geom_bar(position="fill", stat="identity")

ggplot(dataWD_CTRL, aes(fill=Condition, y=Freq, x=Cluster)) + 
    geom_bar(position="fill", stat="identity")

ggplot(dataIS_WD, aes(fill=Condition, y=Freq, x=Cluster)) + 
    geom_bar(position="fill", stat="identity")

#for MT=10%
oligos.integratedOL <- subset(oligos.integrated,seurat_clusters %in% c(1:6,8,9))
# #subset nonOLlineage cells
# nonOL <- colnames(oligos.integratedIm)[oligos.integratedIm@meta.data$seurat_clusters %in% c(3:6,8)]
# oligos.integratedOL <- oligos.integratedOL[,! colnames(oligos.integratedOL) %in% nonOL]
#non-integrated
#oligos.integratedOL <- subset(oligos.integrated,seurat_clusters %in% c(1:6,10))
#for MT=5%
#oligos.integratedOL <- subset(oligos.integrated,seurat_clusters %in% c(1:5))
DefaultAssay(oligos.integrated) <- "SCT"
# oligos.integratedOL <- RunPCA(oligos.integratedOL, verbose = FALSE,features=c("Opalin","Ptgds","Apoe","S100b","Apod","Lamp1","Fos","Sepp1"),npcs=5,approx=FALSE)
oligos.integratedOL <- RunPCA(oligos.integratedOL, verbose = FALSE)#,features=GeneMarkov)
ElbowPlot(oligos.integratedOL)

oligos.integratedOL <- RunUMAP(oligos.integratedOL, dims = 1:30)
21:26:57 UMAP embedding parameters a = 0.9922 b = 1.112
21:26:57 Read 1731 rows and found 30 numeric columns
21:26:57 Using Annoy for neighbor search, n_neighbors = 30
21:26:57 Building Annoy index with metric = cosine, n_trees = 50
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
21:26:58 Writing NN index file to temp file /var/folders/98/j14xl9ln5190l9fvq32g5rkw0000gn/T//RtmpSdWnAZ/file3f2e77e7b5b0
21:26:58 Searching Annoy index using 1 thread, search_k = 3000
21:26:58 Annoy recall = 100%
21:26:59 Commencing smooth kNN distance calibration using 1 thread
21:27:00 Initializing from normalized Laplacian + noise
21:27:01 Commencing optimization for 500 epochs, with 68344 positive edges
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
21:27:05 Optimization finished
#oligos.integratedScience <- RunTSNE(oligos.integratedScience, dims = 1:10)
plots <- DimPlot(oligos.integratedOL, group.by = c("seurat_clusters"), combine = FALSE)
plots <- lapply(X = plots, FUN = function(x) x + theme(legend.position = "top") + guides(color = guide_legend(nrow = 3, 
    byrow = TRUE, override.aes = list(size = 3))))
CombinePlots(plots)
CombinePlots is being deprecated. Plots should now be combined using the patchwork system.

plots <- DimPlot(oligos.integratedOL, group.by = c("Sample"), combine = FALSE)
plots <- lapply(X = plots, FUN = function(x) x + theme(legend.position = "top") + guides(color = guide_legend(nrow = 3, 
    byrow = TRUE, override.aes = list(size = 3))))
CombinePlots(plots)
CombinePlots is being deprecated. Plots should now be combined using the patchwork system.

DimPlot(oligos.integratedOL, group.by = c("Sample"), combine = FALSE)
[[1]]

DimPlot(oligos.integratedOL, group.by = c("seurat_clusters"), combine = FALSE)
[[1]]

DimPlot(oligos.integratedOL, group.by = c("predicted.id"), combine = FALSE,label=TRUE)
[[1]]

oligos.integrated.markersOL %>% group_by(cluster) %>% top_n(n = 2, wt = avg_logFC)
DefaultAssay(oligos.integratedOL) <- "SCT"
# Normalize RNA data for visualization purposes
#oligos.integrated <- NormalizeData(oligos.integrated, verbose = FALSE)
FeaturePlot(oligos.integratedOL, c("Pdgfra", "Ptprz1","Bmp4","Itpr2", "Egr1","Egr2", "Fos","Klk6", "Hopx", "Ptgds","Il33","Mbp","Cd74","Serpina3n"),pt.size = 1)

FeaturePlot(oligos.integratedOL, c("Opalin","Ptgds","Apoe","S100b","Apod","Lamp1","Fos","Sepp1","Klk6","Hopx"),pt.size = 1)
The following requested variables were not found: Sepp1

DefaultAssay(oligos.integrated) <- "SCT"
library(ggrepel)
DefaultAssay(oligos.integratedOL) <- "SCT"
Idents(oligos.integratedOL) <- "Sample"
oligos.integrated.samplediffIS <- FindMarkers(oligos.integratedOL, ident.1 = "IS", ident.2 = "CTRL", verbose = FALSE,logfc.threshold = 0,min.pct=0)
#head(oligos.integrated.samplediffAllRNA, n = 50)
diffmatrix <- oligos.integrated.samplediffIS
diffmatrix$logp_val <- -log10(diffmatrix$p_val_adj)
ggplot(diffmatrix,aes(avg_logFC,y=logp_val,label=row.names(diffmatrix)))+ geom_point(size=0.5)+ geom_text_repel(data=subset(diffmatrix, p_val_adj < 0.01 & abs(avg_logFC) > 0.7),                              label=row.names(subset(diffmatrix, p_val_adj < 0.01 & abs(avg_logFC) > 0.7)))+xlab("log2_FC") + ylab("-log10_p-value_adj") + geom_hline(yintercept=-log10(0.01),linetype="dashed",size=0.5) 

oligos.integrated.samplediffWD <- FindMarkers(oligos.integratedOL, ident.1 = "WD", ident.2 = c("CTRL"), verbose = FALSE,logfc.threshold = 0,min.pct=0)
#head(oligos.integrated.samplediffAllRNA, n = 50)
diffmatrix <- oligos.integrated.samplediffWD
diffmatrix$logp_val <- -log10(diffmatrix$p_val_adj)
ggplot(diffmatrix,aes(avg_logFC,y=logp_val,label=row.names(diffmatrix)))+ geom_point(size=0.5)+ geom_text_repel(data=subset(diffmatrix, p_val_adj < 0.01 & abs(avg_logFC) > 0.35),                              label=row.names(subset(diffmatrix, p_val_adj < 0.01 & abs(avg_logFC) > 0.35)))+xlab("log2_FC") + ylab("-log10_p-value_adj") + geom_hline(yintercept=-log10(0.01),linetype="dashed",size=0.5) 

oligos.integrated.samplediffISWD <- FindMarkers(oligos.integratedOL, ident.1 = "IS", ident.2 = "WD", verbose = FALSE,logfc.threshold = 0,min.pct=0)
#head(oligos.integrated.samplediffAllRNA, n = 50)
diffmatrix <- oligos.integrated.samplediffISWD
diffmatrix$logp_val <- -log10(diffmatrix$p_val_adj)
ggplot(diffmatrix,aes(avg_logFC,y=logp_val,label=row.names(diffmatrix)))+ geom_point(size=0.5)+ geom_text_repel(data=subset(diffmatrix, p_val_adj < 0.01 & abs(avg_logFC) > 0.85),                              label=row.names(subset(diffmatrix, p_val_adj < 0.01 & abs(avg_logFC) > 0.85)))+xlab("log2_FC") + ylab("-log10_p-value_adj") + geom_hline(yintercept=-log10(0.01),linetype="dashed",size=0.5) 

oligos.integrated.samplediffISWDvsCNTRL <- FindMarkers(oligos.integratedOL, ident.1 = c("IS","WD"), ident.2 = "CTRL", verbose = FALSE,logfc.threshold = 0,min.pct=0)
#head(oligos.integrated.samplediffAllRNA, n = 50)
diffmatrix <- oligos.integrated.samplediffISWDvsCNTRL
diffmatrix$logp_val <- -log10(diffmatrix$p_val_adj)
ggplot(diffmatrix,aes(avg_logFC,y=logp_val,label=row.names(diffmatrix)))+ geom_point(size=0.5)+ geom_text_repel(data=subset(diffmatrix, p_val_adj < 0.01 & abs(avg_logFC) > 0.85),                              label=row.names(subset(diffmatrix, p_val_adj < 0.01 & abs(avg_logFC) > 0.85)))+xlab("log2_FC") + ylab("-log10_p-value_adj") + geom_hline(yintercept=-log10(0.01),linetype="dashed",size=0.5) 

DefaultAssay(oligos.integrated) <- "SCT"
DiffMatrix <- list()
diffmatrixnames <- c("oligos.integrated.samplediffISWD",
                    "oligos.integrated.samplediffISWDvsCNTRL")
                     
do.call(head,as.list(as.name(diffmatrixnames[1])))
DiffMatrix <- list()
diffmatrixnames <- c("oligos.integrated.samplediffISWD",
                    "oligos.integrated.samplediffISWDvsCNTRL")
                     
do.call(head,as.list(as.name(diffmatrixnames[1])))
library(clusterProfiler)
#Convert to gencode using biomart
library(biomaRt)
listMarts()
ensembl = useMart("ensembl",dataset="mmusculus_gene_ensembl")
listDatasets(ensembl)
attributes = listAttributes(ensembl)
Biomart_gencode_ensembl84_biotypes <- getBM(attributes=c("mgi_symbol","ensembl_gene_id","entrezgene_id","gene_biotype"), filters = "", values = "", ensembl)
Biomart_gencode_ensembl84_biotypes[, 'gene_biotype'] <- as.factor(Biomart_gencode_ensembl84_biotypes[,'gene_biotype'])
#Filter for only our genes
 Biotype_All_dataset <- subset(Biomart_gencode_ensembl84_biotypes, mgi_symbol %in% oligos.integrated@assays$SCT@var.features)
entrezID <-  subset(Biotype_All_dataset, Biotype_All_dataset$mgi_symbol %in% oligos.integrated@assays$SCT@var.features)
# if (!requireNamespace("BiocManager", quietly = TRUE))
#     install.packages("BiocManager")
# 
# BiocManager::install("reactome.db")
library(ReactomePA)
library(org.Mm.eg.db)
ReactomeTerms <- list()
i=1
#UP
pvaladj <- 0.01
logfc <- 0.25
for(i in 1:length(diffmatrixnames)){
diffmatrix <- do.call("as.data.frame",as.list(as.name(diffmatrixnames[i])))
diffmatrix <- subset(diffmatrix, p_val_adj < pvaladj & avg_logFC > logfc)
siggenes <- head(row.names(diffmatrix),50)
entrezmatched <- entrezID[entrezID$mgi_symbol %in% siggenes,]
#entrezID <- entrezID[! apply(entrezID[,c(1,3)], 1,function (x) anyNA(x)),]
allLLIDs <- entrezmatched$entrezgene
modulesReactome <- enrichPathway(gene=allLLIDs,organism="mouse",pvalueCutoff=0.01,qvalueCutoff = 0.3,pAdjustMethod = "none", readable=T)
ReactomeTerms[[i]] <- modulesReactome
head(as.data.frame(modulesReactome))
print(i)
}
[1] 1
[1] 2
ReactomeTerms[which(lapply(ReactomeTerms,function(x) is.null(x))==TRUE)] <- "No_Genes"
#Add DOWN 
pvaladj <- 0.01
logfc <- -0.25
offset <- length(ReactomeTerms)
for(i in 1:length(diffmatrixnames)){
  i=i+offset
diffmatrix <- do.call("as.data.frame",as.list(as.name(diffmatrixnames[i-offset])))
diffmatrix <- subset(diffmatrix, p_val_adj < pvaladj & avg_logFC < logfc)
siggenes <- head(row.names(diffmatrix),50)
entrezmatched <- entrezID[entrezID$mgi_symbol %in% siggenes,]
#entrezID <- entrezID[! apply(entrezID[,c(1,3)], 1,function (x) anyNA(x)),]
allLLIDs <- entrezmatched$entrezgene
modulesReactome <- enrichPathway(gene=allLLIDs,organism="mouse",pvalueCutoff=0.01,qvalueCutoff = 0.3,pAdjustMethod = "none", readable=T)
ReactomeTerms[[i]] <- modulesReactome
head(as.data.frame(modulesReactome))
print(i)
}
[1] 3
[1] 4
ReactomeTerms[which(lapply(ReactomeTerms,function(x) is.null(x))==TRUE)] <- "No_Genes"
Upper_diff <- subset(oligos.integrated.samplediffISWD, p_val_adj < 0.01 & abs(avg_logFC) > 0)
Lower_diff <- subset(oligos.integrated.samplediffISWDvsCNTRL, p_val_adj < 0.01 & abs(avg_logFC) > 0)
AlldiffgenesHetMOL5 <- intersect(intersect(row.names(oligos.integrated.samplediffISWD),row.names(oligos.integrated.samplediffISWDvsCNTRL)),unique(c(row.names(Upper_diff),row.names(Lower_diff))))
subset2 <- oligos.integrated.samplediffISWD[AlldiffgenesHetMOL5,]
subset3 <- oligos.integrated.samplediffISWDvsCNTRL[AlldiffgenesHetMOL5,]
subsetMOL5 <- cbind(subset2,subset3)
colnames(subsetMOL5) <- make.unique(colnames(subsetMOL5))
diffmatrix <- subsetMOL5
diffmatrix$log_p_val <- -log10(diffmatrix$p_val_adj)
q95pgenes1 <- row.names(diffmatrix[which(diffmatrix$log_p_val >= quantile(diffmatrix$log_p_val,0)),])
diffmatrix$log_p_val.1 <- -log10(diffmatrix$p_val_adj.1)
q95pgenes2 <- row.names(diffmatrix[which(diffmatrix$log_p_val.1 >= quantile(diffmatrix$log_p_val.1,0)),])
q95pgenes <- unique(c(q95pgenes1,q95pgenes2))
diffmatrix <- diffmatrix[q95pgenes,]
diffmatrix$avg_logFC[is.infinite(diffmatrix$avg_logFC)] <- max(diffmatrix$avg_logFC[!is.infinite(diffmatrix$avg_logFC)])
diffmatrix$avg_logFC.1[is.infinite(diffmatrix$avg_logFC.1)] <- max(diffmatrix$avg_logFC.1[!is.infinite(diffmatrix$avg_logFC.1)])
#diffmatrix$avg_logFC.1 <- 2*diffmatrix$avg_logFC.1
diffmatrix$combp <- -log10(diffmatrix$p_val_adj*diffmatrix$p_val_adj.1)
diffmatrix$maxp <- apply(cbind(diffmatrix$log_p_val,diffmatrix$log_p_val.1),1,function(x) max(x))
diffmatrix$minp <- apply(cbind(diffmatrix$p_val_adj,diffmatrix$p_val_adj.1),1,function(x) min(x))
diffmatrix$maxp[is.infinite(diffmatrix$maxp)] <- max(diffmatrix$maxp[!is.infinite(diffmatrix$maxp)])
diffmatrix$maxFC <- apply(cbind(diffmatrix$avg_logFC,diffmatrix$avg_logFC.1),1,function(x) max(abs(x))) 
diffmatrix$Genes <- factor(row.names(diffmatrix),levels=row.names(diffmatrix))
ggplot(diffmatrix,aes(avg_logFC,y=avg_logFC.1,colour=maxp,label=row.names(diffmatrix)))+ geom_point(size=diffmatrix$maxp/100) + scale_colour_viridis_c(direction = +1,option ="viridis" ) + geom_hline(yintercept= 0,linetype="dashed",size=0.1,color="cyan")+
  geom_hline(yintercept= 0.25,linetype="dashed",size=0.1,color="grey",alpha=0.5)+
  geom_hline(yintercept= -0.25,linetype="dashed",size=0.1,color="grey",alpha=0.5)+
  geom_vline(xintercept= 0,linetype="dashed",size=0.1,color="cyan")+
  geom_vline(xintercept= 0.25,linetype="dashed",size=0.1,color="grey",alpha=0.5)+
  geom_vline(xintercept= -0.25,linetype="dashed",size=0.1,color="grey",alpha=0.5)+
  geom_text_repel(size=3,fontface = "bold",force=1,data=subset(diffmatrix, 
maxp > quantile(diffmatrix$maxp,0.98) #| 
# avg_logFC > 0 |
# avg_logFC < -0|
# avg_logFC.1 > 0 |
# avg_logFC.1 < -0)
),label=row.names(subset(diffmatrix, 
maxp > quantile(diffmatrix$maxp,0.98) #| 
# avg_logFC > 0 |
# avg_logFC < -0 |
# avg_logFC.1 > 0 |
)# avg_logFC.1 < -0)
))+xlab("IS vs WD") + ylab("Other vs Control") +theme(
  # get rid of panel grids
  panel.grid.major = element_blank(),
  #panel.grid.major = element_line(color="darkgrey",size=0.1),
  panel.grid.minor = element_blank(),
  #panel.grid.minor = element_line(color="darkgrey",size=0.05),
  # Change plot and panel background
  plot.background=element_rect(fill = "white"),
  panel.background = element_rect(fill = 'black'),
  # Change legend 
  legend.background = element_rect(fill = "white", color = NA),
  legend.key = element_rect(color = "gray", fill = "white"),
  legend.title = element_text(color = "Black"),
  legend.text = element_text(color = "black")
  )

#magma,inferno, plasma,viridis
#scale_colour_gradient(low = "darkgreen", high = "red")
#Do reactome analysis at the bottom of script
i=1
j=1
#for(i in 1:length(ReactomeTerms)){
for(i in 1:4){
pwydata <- as.data.frame(ReactomeTerms[[i]])
geneset <- strsplit(pwydata$geneID, "/")
FCmeans <- data.frame()
for(j in 1:length(geneset)){
 geneset2FC <- which(row.names(diffmatrix) %in% geneset[[j]])
 FC <- mean(diffmatrix$avg_logFC[geneset2FC],na.rm=T)
 FCvar <- var(diffmatrix$avg_logFC[geneset2FC],na.rm=T)
 FC.1 <- mean(diffmatrix$avg_logFC.1[geneset2FC],na.rm=T)
 FC.1var <- var(diffmatrix$avg_logFC.1[geneset2FC],na.rm=T)
FCmeans <- rbind(FCmeans,cbind(FC,FC.1,FCvar,FC.1var))
 print(j)
}
ReactomeTerms[[i]] <- cbind(ReactomeTerms[[i]],FCmeans)
print(i)
}
[1] 1
[1] 2
[1] 3
[1] 4
[1] 5
[1] 6
[1] 7
[1] 8
[1] 9
[1] 10
[1] 11
[1] 12
[1] 13
[1] 14
[1] 15
[1] 16
[1] 1
[1] 1
[1] 2
[1] 3
[1] 4
[1] 5
[1] 6
[1] 7
[1] 8
[1] 9
[1] 10
[1] 11
[1] 12
[1] 13
[1] 14
[1] 15
[1] 16
[1] 17
[1] 18
[1] 19
[1] 20
[1] 21
[1] 22
[1] 23
[1] 24
[1] 25
[1] 26
[1] 27
[1] 28
[1] 29
[1] 30
[1] 31
[1] 32
[1] 33
[1] 34
[1] 35
[1] 36
[1] 37
[1] 38
[1] 39
[1] 40
[1] 41
[1] 42
[1] 43
[1] 44
[1] 45
[1] 46
[1] 47
[1] 48
[1] 2
[1] 1
[1] 2
[1] 3
[1] 4
[1] 5
[1] 6
[1] 7
[1] 8
[1] 9
[1] 10
[1] 3
[1] 1
[1] 2
[1] 3
[1] 4
pathmatrix <- rbind(as.data.frame(ReactomeTerms[[1]]),as.data.frame(ReactomeTerms[[2]]),as.data.frame(ReactomeTerms[[3]]),as.data.frame(ReactomeTerms[[4]]))
#pathmatrix <- rbind(as.data.frame(ReactomeTerms[[1]]),as.data.frame(ReactomeTerms[[2]]))
#pathmatrix <- rbind(as.data.frame(ReactomeTerms[[3]]),as.data.frame(ReactomeTerms[[4]]))
pathmatrix$p.adjust_original <- pathmatrix$p.adjust
pathmatrix$p.adjust <- -log10(pathmatrix$p.adjust )
pathmatrix$maxFC <- sum(abs(pathmatrix$FC),abs(pathmatrix$FC.1))
pathmatrix <- subset(pathmatrix, pathmatrix$Count > 1)
pathmatrix$AdjSelect <- pathmatrix$p.adjust*(500*(0.2+abs(pathmatrix$FC)))
#scale_colour_gradient(low = "yellow", high = "red") +
#scale_colour_viridis_c(direction = -1)
#scale_colour_gradient(low = "black", high = "red")
ggplot(pathmatrix,aes(FC,y=FC.1,colour=p.adjust_original),label=pathmatrix$Description)+ geom_point(size=pathmatrix$Count,alpha=0.5) +scale_colour_viridis_c(direction = +1,option = "viridis") +
  geom_hline(yintercept= 0,linetype="solid",size=0.5,color="black",alpha=0.5)+
  geom_hline(yintercept= 0.25,linetype="solid",size=0.2,color="black",alpha=0.5)+
  geom_hline(yintercept= -0.25,linetype="solid",size=0.2,color="black",alpha=0.5)+
  geom_vline(xintercept= 0,linetype="solid",size=0.5,color="black",alpha=0.5)+
  geom_vline(xintercept= 0.25,linetype="solid",size=0.2,color="black",alpha=0.5)+
  geom_vline(xintercept= -0.25,linetype="solid",size=0.2,color="black",alpha=0.5)+
  geom_text_repel(size=2,fontface="bold",force=20,data=
subset(pathmatrix, 
abs(pathmatrix$AdjSelect) > quantile(
abs(pathmatrix$AdjSelect),1,na.rm=T) | abs(pathmatrix$p.adjust) > quantile(
abs(pathmatrix$p.adjust),0.75,na.rm=T) |
  abs(pathmatrix$FC.1) > quantile(abs(pathmatrix$FC.1),1,na.rm=T)),
label=subset(pathmatrix, 
abs(pathmatrix$AdjSelect) > quantile(abs(pathmatrix$AdjSelect),1,na.rm=T) |  
  abs(pathmatrix$p.adjust) > quantile(abs(pathmatrix$p.adjust),0.75,na.rm=T) |
  abs(pathmatrix$FC.1) > quantile(abs(pathmatrix$FC.1),1,na.rm=T))$Description,box.padding = 0.5)+xlab("IS vs WD") + ylab("Other vs Control") 

pathmatrixsort <- pathmatrix[order(pathmatrix$FC,decreasing=T),]
pathmatrixsort$Description <- factor(pathmatrixsort$Description, levels = unique(pathmatrixsort$Description)) 
ggplot(pathmatrixsort, aes(x=FC, y=Description)) +
        geom_point(aes(color = p.adjust_original))

pathmatrixsort <- pathmatrix[order(pathmatrix$FC.1,decreasing=T),]
pathmatrixsort$Description <- factor(pathmatrixsort$Description, levels = unique(pathmatrixsort$Description)) 
ggplot(pathmatrixsort, aes(x=FC.1, y=Description)) +
        geom_point(aes(color = p.adjust_original))

pathmatrixsort <- pathmatrix[order(pathmatrix$FC,decreasing=F),]
pathmatrixsort$Description <- factor(pathmatrixsort$Description, levels = unique(pathmatrixsort$Description)) 
pathmatrixsort <- pathmatrixsort[!pathmatrixsort$p.adjust_original > 0.01,]
library(reshape2)
pathmatrixsortISWD <- pathmatrixsort[,c(2,10,14)]
pathmatrixsortISWD$Group <- rep("ISvsWD",nrow(pathmatrixsortISWD))
pathmatrixsortCntrlISWD <- pathmatrixsort[,c(2,11,14)]
colnames(pathmatrixsortCntrlISWD)[2] <- "FC"
pathmatrixsortCntrlISWD$Group <- rep("ControlvsIS-WD",nrow(pathmatrixsortCntrlISWD))
pathmatrixsort <- rbind(pathmatrixsortISWD,pathmatrixsortCntrlISWD)
ggplot(pathmatrixsort, aes(x=FC, y=Description)) +
        geom_line(aes(group = Description)) +
        geom_point(aes(color = Group))

pathmatrixsort <- pathmatrix[order(pathmatrix$FC,decreasing=F),]
pathmatrixsort$Description <- factor(pathmatrixsort$Description, levels = unique(pathmatrixsort$Description)) 
pathmatrixsort <- pathmatrixsort[!pathmatrixsort$p.adjust_original > 0.01,]
library(reshape2)
ggplot(pathmatrixsort, aes(x=FC, y=Description)) +
        geom_point(aes(color = Count))

pathmatrixsort <- pathmatrix[order(pathmatrix$FC.1,decreasing=F),]
pathmatrixsort$Description <- factor(pathmatrixsort$Description, levels = unique(pathmatrixsort$Description)) 
pathmatrixsort <- pathmatrixsort[!pathmatrixsort$p.adjust_original > 0.01,]
library(reshape2)
ggplot(pathmatrixsort, aes(x=FC.1, y=Description)) +
        geom_point(aes(color = Count))

#Coexpression MOL5 and MOL6 vs MOL1
oligos.integratedOL256 <- subset(oligos.integrated,predicted.id %in% c("MOL2","MOL5","MOL6"))
Ptgdsexpression <- oligos.integratedOL256@assays$RNA@counts["Ptgds",]
Klk6expression <- oligos.integratedOL256@assays$RNA@counts["Klk6",]
ExpressionCombo <- as.data.frame(t(oligos.integratedOL256@assays$RNA@counts[c("Ptgds","Klk6"),]))
#plot(x=log(ExpressionCombo$Ptgds+1),y=log(ExpressionCombo$Klk6+1))
ExpressionCombosorted <- ExpressionCombo[order(ExpressionCombo$Ptgds,decreasing = TRUE),]
barplot(ExpressionCombosorted$Ptgds)

barplot(ExpressionCombosorted$Klk6)

ExpressionCombosorted <- ExpressionCombo[order(ExpressionCombo$Klk6,decreasing = FALSE),]
barplot(ExpressionCombosorted$Ptgds)

barplot(ExpressionCombosorted$Klk6)

namesMOL56 <- oligos.integratedOL256@meta.data$predicted.id 
plot(x=ExpressionCombo$Ptgds,y=oligos.integratedOL256@meta.data$prediction.score.MOL5)

plot(x=ExpressionCombo$Ptgds,y=oligos.integratedOL256@meta.data$prediction.score.MOL6)

ExpressionCombosorted <- ExpressionCombo[order(ExpressionCombo$Ptgds,decreasing = TRUE),]
MOL6pred <- oligos.integratedOL256@meta.data$prediction.score.MOL6
names(MOL6pred) <- row.names(oligos.integratedOL256@meta.data)
barplot(MOL6pred[row.names(ExpressionCombosorted)])

MOL5pred <- oligos.integratedOL256@meta.data$prediction.score.MOL5
names(MOL5pred) <- row.names(oligos.integratedOL256@meta.data)
barplot(MOL5pred[row.names(ExpressionCombosorted)])

MOL56pred <- rowMeans(cbind(MOL5pred,MOL6pred))
barplot(MOL56pred[row.names(ExpressionCombosorted)])

plot(MOL56pred,ExpressionCombosorted$Ptgds)

ExpressionCombosorted <- ExpressionCombo[order(ExpressionCombo$Klk6,decreasing = FALSE),]
MOL2pred <- oligos.integratedOL256@meta.data$prediction.score.MOL2
names(MOL2pred) <- row.names(oligos.integratedOL256@meta.data)
barplot(MOL2pred[row.names(ExpressionCombosorted)])

ExpressionCombo <- as.data.frame(t(oligos.integratedOL256@assays$RNA@counts[c("Ptgds","Klk6"),]))
pred <- (MOL56pred)-(MOL2pred)
ExpressionCombosorted <- ExpressionCombo[order(ExpressionCombo$Ptgds,decreasing = TRUE),]
barplot(ExpressionCombosorted$Ptgds)

barplot(pred[row.names(ExpressionCombosorted)])

ExpressionCombosorted <- ExpressionCombo[order(ExpressionCombo$Klk6,decreasing = FALSE),]
barplot(ExpressionCombosorted$Klk6)

barplot(pred[row.names(ExpressionCombosorted)])

ExpressionCombo <- as.data.frame(t(oligos.integratedOL256@assays$RNA@counts[c("Ptgds","Klk6"),]))
MOL56ID <-  1*oligos.integratedOL256@meta.data$predicted.id %in% c("MOL5","MOL6")
MOL2ID <-  -1*oligos.integratedOL256@meta.data$predicted.id %in% c("MOL2")
MOLID <- MOL56ID+MOL2ID
names(MOLID) <- row.names(oligos.integratedOL256@meta.data)
ExpressionCombosorted <- ExpressionCombo[order(ExpressionCombo$Ptgds,decreasing = TRUE),]
barplot(ExpressionCombosorted$Ptgds)

barplot(MOLID[row.names(ExpressionCombosorted)])

ExpressionCombosorted <- ExpressionCombo[order(ExpressionCombo$Klk6,decreasing = TRUE),]
barplot(ExpressionCombosorted$Klk6)

barplot(MOLID[row.names(ExpressionCombosorted)])

ExpressionCombo <- as.data.frame(t(oligos.integratedOL256@assays$RNA@counts[c("Ptgds","Klk6"),]))
MOLID <- droplevels(oligos.integratedOL256@meta.data$predicted.id)
names(MOLID) <- row.names(oligos.integratedOL256@meta.data)
ExpressionCombosorted <- ExpressionCombo[order(ExpressionCombo$Ptgds,decreasing = TRUE),]
ExpressionCombosorted$ID <- MOLID[row.names(ExpressionCombosorted)] 
ExpressionCombosorted$order <- seq_len(nrow(ExpressionCombosorted))
ggplot(ExpressionCombosorted, aes(x=order, fill=ID)) +
  geom_density(alpha=0.4)

ExpressionCombo <- as.data.frame(t(oligos.integratedOL256@assays$RNA@counts[c("Ptgds","Klk6","S100b","Opalin","Hopx","Apoe","Apod"),]))
MOLID <- droplevels(oligos.integratedOL256@meta.data$predicted.id)
names(MOLID) <- row.names(oligos.integratedOL256@meta.data)
ExpressionCombosorted <- ExpressionCombo[order(ExpressionCombo$Klk6,decreasing = TRUE),]
ExpressionCombosorted$ID <- MOLID[row.names(ExpressionCombosorted)] 
ExpressionCombosorted$order <- seq_len(nrow(ExpressionCombosorted))
ggplot(ExpressionCombosorted, aes(x=order, fill=ID)) +
  geom_density(alpha=0.4)

ggplot(ExpressionCombosorted, aes(x=log(Ptgds+1), fill=ID)) +
  geom_density(alpha=0.4)

ggplot(ExpressionCombosorted, aes(x=log(Klk6+1), fill=ID)) +
  geom_density(alpha=0.4)

ggplot(ExpressionCombosorted, aes(x=log(S100b+1), fill=ID)) +
  geom_density(alpha=0.4)

ggplot(ExpressionCombosorted, aes(x=log(Opalin+1), fill=ID)) +
  geom_density(alpha=0.4)

ggplot(ExpressionCombosorted, aes(x=log(Hopx+1), fill=ID)) +
  geom_density(alpha=0.4)

ggplot(ExpressionCombosorted, aes(x=log(Apoe+1), fill=ID)) +
  geom_density(alpha=0.4)

ggplot(ExpressionCombosorted, aes(x=log(Apod+1), fill=ID)) +
  geom_density(alpha=0.4)

ggplot(ExpressionCombosorted, aes(x=log(Klk6+1),y=log(Ptgds+1), color=ID)) +
  geom_point(alpha=0.4)

ggplot(ExpressionCombosorted, aes(x=Ptgds, fill=ID)) +
  geom_density(alpha=0.4)

ggplot(ExpressionCombosorted, aes(x=Klk6, fill=ID)) +
  geom_density(alpha=0.4)

LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKYGBge3IgZWNobz1GQUxTRX0KbGlicmFyeShTZXVyYXQpCmxpYnJhcnkoZ2dwbG90MikKb3B0aW9ucyhmdXR1cmUuZ2xvYmFscy5tYXhTaXplID0gNDAwMCAqIDEwMjReMikKI0xvYWQgZGF0YQpwYXRocyA8LSBjKCIvVm9sdW1lcy9DYXN0ZWxvLUJyYW5jby9VUFBNQVhfSU5CT1hfQkFDS1VQL0lOQk9YLzEwWF8xOV9HQ0JfMTVfUjQyLzEwWF8xOV8wNzMvb3V0cy9maWx0ZXJlZF9mZWF0dXJlX2JjX21hdHJpeC8iLAogICAgICAgICAgICIvVm9sdW1lcy9DYXN0ZWxvLUJyYW5jby9VUFBNQVhfSU5CT1hfQkFDS1VQL0lOQk9YLzEwWF8xOV9HQ0JfMTVfUjQyLzEwWF8xOV8wNzQvb3V0cy9maWx0ZXJlZF9mZWF0dXJlX2JjX21hdHJpeC8iLAogICAgICAgICAgICIvVm9sdW1lcy9DYXN0ZWxvLUJyYW5jby9VUFBNQVhfSU5CT1hfQkFDS1VQL0lOQk9YLzEwWF8xOV9HQ0JfMTVfUjQyLzEwWF8xOV8wNzUvb3V0cy9maWx0ZXJlZF9mZWF0dXJlX2JjX21hdHJpeC8iKQpuYW1lcyhwYXRocykgPC0gYygiSVMiLCJDVFJMIiwiV0QiKQpTQyA8LSBSZWFkMTBYKGRhdGEuZGlyID0gcGF0aHMsIGdlbmUuY29sdW1uID0gMiwgdW5pcXVlLmZlYXR1cmVzID0gVFJVRSkKU2FtcGxlcyA8LSBzdHJzcGxpdCh1bmxpc3QoU0NARGltbmFtZXNbMl0pLCJfIikKU2FtcGxlcyA8LSB1bmxpc3QobGFwcGx5KFNhbXBsZXMsZnVuY3Rpb24oeCkgeFsxXSkpCm5hbWVzKFNhbXBsZXMpIDwtIGNvbG5hbWVzKFNDKQp0YWJsZShTYW1wbGVzKSAKZW1hdF8xMHggPC0gU0MKYW5ub18xMHggPC0gU2FtcGxlcwphbm5vXzEweCA8LSBhcy5kYXRhLmZyYW1lKGFubm9fMTB4LHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKCmNvbG5hbWVzKGFubm9fMTB4KSA8LSAiU2FtcGxlIgojUHV0IGluIFNldXJhdCBvYmplY3QgYW5kIHNwbGl0IGluIHR3byB0byBwZXJmb3JtIHByZXBub3JtYWxpemF0aW9uCm9saWdvcyA8LSBDcmVhdGVTZXVyYXRPYmplY3QoZW1hdF8xMHgsIG1ldGEuZGF0YSA9ICBhbm5vXzEweCxtaW4uY2VsbHMgPSAzLCBtaW4uZmVhdHVyZXMgPSAyMDApCmBgYApgYGB7ciBlY2hvPVRSVUUsIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTZ9CiMgVGhlIFtbIG9wZXJhdG9yIGNhbiBhZGQgY29sdW1ucyB0byBvYmplY3QgbWV0YWRhdGEuIFRoaXMgaXMgYSBncmVhdCBwbGFjZSB0byBzdGFzaCBRQyBzdGF0cwpvbGlnb3NbWyJwZXJjZW50Lm10Il1dIDwtIFBlcmNlbnRhZ2VGZWF0dXJlU2V0KG9saWdvcywgcGF0dGVybiA9ICJebXQtIikKIyBWaXN1YWxpemUgUUMgbWV0cmljcyBhcyBhIHZpb2xpbiBwbG90ClZsblBsb3Qob2xpZ29zLCBncm91cC5ieSA9ICJTYW1wbGUiLGZlYXR1cmVzID0gYygibkZlYXR1cmVfUk5BIiwgIm5Db3VudF9STkEiLCAicGVyY2VudC5tdCIpLCBuY29sID0gMSxwdC5zaXplID0gMC4xKQpgYGAKYGBge3IgZWNobz1UUlVFLCBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD0xMH0KIyBGZWF0dXJlU2NhdHRlciBpcyB0eXBpY2FsbHkgdXNlZCB0byB2aXN1YWxpemUgZmVhdHVyZS1mZWF0dXJlIHJlbGF0aW9uc2hpcHMsIGJ1dCBjYW4gYmUgdXNlZAojIGZvciBhbnl0aGluZyBjYWxjdWxhdGVkIGJ5IHRoZSBvYmplY3QsIGkuZS4gY29sdW1ucyBpbiBvYmplY3QgbWV0YWRhdGEsIFBDIHNjb3JlcyBldGMuCnBsb3QxIDwtIEZlYXR1cmVTY2F0dGVyKG9saWdvcywgZ3JvdXAuYnkgPSAiU2FtcGxlIixmZWF0dXJlMSA9ICJuQ291bnRfUk5BIiwgZmVhdHVyZTIgPSAicGVyY2VudC5tdCIscHQuc2l6ZSA9IDAuNSkKcGxvdDIgPC0gRmVhdHVyZVNjYXR0ZXIob2xpZ29zLCBncm91cC5ieSA9ICJTYW1wbGUiLCBmZWF0dXJlMSA9ICJuQ291bnRfUk5BIiwgZmVhdHVyZTIgPSAibkZlYXR1cmVfUk5BIixwdC5zaXplID0gMC41KQpDb21iaW5lUGxvdHMocGxvdHMgPSBsaXN0KHBsb3QxLCBwbG90MikpCmBgYApgYGB7cn0KI0NsZWFuIHVwIHRoZSBkYXRhCm9saWdvcyA8LSBzdWJzZXQob2xpZ29zLCBzdWJzZXQgPSBuRmVhdHVyZV9STkEgPiA1MDAgJiBuRmVhdHVyZV9STkEgPCA3MDAwICYgcGVyY2VudC5tdCA8IDEwKQpuY29sKG9saWdvcykKYGBgCmBgYHtyIGVjaG89VFJVRSwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9Nn0KIyBUaGUgW1sgb3BlcmF0b3IgY2FuIGFkZCBjb2x1bW5zIHRvIG9iamVjdCBtZXRhZGF0YS4gVGhpcyBpcyBhIGdyZWF0IHBsYWNlIHRvIHN0YXNoIFFDIHN0YXRzCm9saWdvc1tbInBlcmNlbnQubXQiXV0gPC0gUGVyY2VudGFnZUZlYXR1cmVTZXQob2xpZ29zLCBwYXR0ZXJuID0gIl5tdC0iKQojIFZpc3VhbGl6ZSBRQyBtZXRyaWNzIGFzIGEgdmlvbGluIHBsb3QKVmxuUGxvdChvbGlnb3MsIGdyb3VwLmJ5ID0gIlNhbXBsZSIsZmVhdHVyZXMgPSBjKCJuRmVhdHVyZV9STkEiLCAibkNvdW50X1JOQSIsICJwZXJjZW50Lm10IiksIG5jb2wgPSAxLHB0LnNpemUgPSAwLjEpCmBgYApgYGB7ciBlY2hvPVRSVUUsIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTEwfQojIEZlYXR1cmVTY2F0dGVyIGlzIHR5cGljYWxseSB1c2VkIHRvIHZpc3VhbGl6ZSBmZWF0dXJlLWZlYXR1cmUgcmVsYXRpb25zaGlwcywgYnV0IGNhbiBiZSB1c2VkCiMgZm9yIGFueXRoaW5nIGNhbGN1bGF0ZWQgYnkgdGhlIG9iamVjdCwgaS5lLiBjb2x1bW5zIGluIG9iamVjdCBtZXRhZGF0YSwgUEMgc2NvcmVzIGV0Yy4KcGxvdDEgPC0gRmVhdHVyZVNjYXR0ZXIob2xpZ29zLCBncm91cC5ieSA9ICJTYW1wbGUiLGZlYXR1cmUxID0gIm5Db3VudF9STkEiLCBmZWF0dXJlMiA9ICJwZXJjZW50Lm10IixwdC5zaXplID0gMC41KQpwbG90MiA8LSBGZWF0dXJlU2NhdHRlcihvbGlnb3MsIGdyb3VwLmJ5ID0gIlNhbXBsZSIsIGZlYXR1cmUxID0gIm5Db3VudF9STkEiLCBmZWF0dXJlMiA9ICJuRmVhdHVyZV9STkEiLHB0LnNpemUgPSAwLjUpCkNvbWJpbmVQbG90cyhwbG90cyA9IGxpc3QocGxvdDEsIHBsb3QyKSkKYGBgCk5vdyB3ZSBub3JtYWxpemUgdGhlIGRhdGFzZXQuCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGluY2x1ZGU9RkFMU0UsIHBhZ2VkLnByaW50PUZBTFNFfQojb2xpZ29zLmludGVncmF0ZWQgPC0gU0NUcmFuc2Zvcm0ob2xpZ29zLHZlcmJvc2UgPSBGQUxTRSkKIyBvbGlnb3MubGlzdCA8LSBTcGxpdE9iamVjdChvbGlnb3MsIHNwbGl0LmJ5ID0gIlNhbXBsZSIpCiMgZm9yIChpIGluIDE6bGVuZ3RoKG9saWdvcy5saXN0KSkgewojICAgICBvbGlnb3MubGlzdFtbaV1dIDwtIFNDVHJhbnNmb3JtKG9saWdvcy5saXN0W1tpXV0sIHZlcmJvc2UgPSBGQUxTRSkKIyB9CiMgb2xpZ29zLmludGVncmF0ZWQgPC0gbWVyZ2Uob2xpZ29zLmxpc3RbWzFdXSxvbGlnb3MubGlzdCxtZXJnZS5kYXRhID0gVFJVRSkKI3VzZSBjb2RlIGJlbG93IHdoZW4gbm8gaW50ZWdyYXRpb24gaXMgbmVlZGVkCm9saWdvcy5pbnRlZ3JhdGVkPC0gU0NUcmFuc2Zvcm0ob2xpZ29zLCB2ZXJib3NlID0gRkFMU0UpCmBgYApgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQojaW50ZWdyYXRlCm9saWdvcy5mZWF0dXJlcyA8LSBTZWxlY3RJbnRlZ3JhdGlvbkZlYXR1cmVzKG9iamVjdC5saXN0ID0gb2xpZ29zLmxpc3QsIG5mZWF0dXJlcyA9IDMwMDApCm9saWdvcy5saXN0IDwtIFByZXBTQ1RJbnRlZ3JhdGlvbihvYmplY3QubGlzdCA9IG9saWdvcy5saXN0LCBhbmNob3IuZmVhdHVyZXMgPSBvbGlnb3MuZmVhdHVyZXMsIAogICAgdmVyYm9zZSA9IEZBTFNFKQpvbGlnb3MuYW5jaG9ycyA8LSBGaW5kSW50ZWdyYXRpb25BbmNob3JzKG9iamVjdC5saXN0ID0gb2xpZ29zLmxpc3QsIG5vcm1hbGl6YXRpb24ubWV0aG9kID0gIlNDVCIsIAogICAgYW5jaG9yLmZlYXR1cmVzID0gb2xpZ29zLmZlYXR1cmVzLCB2ZXJib3NlID0gRkFMU0UpCm9saWdvcy5pbnRlZ3JhdGVkIDwtIEludGVncmF0ZURhdGEoYW5jaG9yc2V0ID0gb2xpZ29zLmFuY2hvcnMsIG5vcm1hbGl6YXRpb24ubWV0aG9kID0gIlNDVCIsIAogICAgdmVyYm9zZSA9IEZBTFNFKQpgYGAKR2VuZXJhdGluZyB0aGUgVU1BUCBhbmQgVFNORS4KYGBge3J9CiNEZWZhdWx0QXNzYXkob2xpZ29zLmludGVncmF0ZWQpIDwtICJpbnRlZ3JhdGVkIgpvbGlnb3MuaW50ZWdyYXRlZCA8LSBSdW5QQ0Eob2xpZ29zLmludGVncmF0ZWQsIHZlcmJvc2UgPSBGQUxTRSkKb2xpZ29zLmludGVncmF0ZWQgPC0gUnVuVU1BUChvbGlnb3MuaW50ZWdyYXRlZCwgZGltcyA9IDE6MzApCiNvbGlnb3MuaW50ZWdyYXRlZCA8LSBSdW5UU05FKG9saWdvcy5pbnRlZ3JhdGVkLCBkaW1zID0gMTozMCkKcGxvdHMgPC0gRGltUGxvdChvbGlnb3MuaW50ZWdyYXRlZCwgZ3JvdXAuYnkgPSBjKCJTYW1wbGUiKSwgY29tYmluZSA9IEZBTFNFKQpwbG90cyA8LSBsYXBwbHkoWCA9IHBsb3RzLCBGVU4gPSBmdW5jdGlvbih4KSB4ICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpICsgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfbGVnZW5kKG5yb3cgPSAzLCAKICAgIGJ5cm93ID0gVFJVRSwgb3ZlcnJpZGUuYWVzID0gbGlzdChzaXplID0gMykpKSkKQ29tYmluZVBsb3RzKHBsb3RzKQojIHBsb3RzIDwtIFRTTkVQbG90KG9saWdvcy5pbnRlZ3JhdGVkLCBncm91cC5ieSA9IGMoIlNhbXBsZSIpLCBjb21iaW5lID0gRkFMU0UpCiMgcGxvdHMgPC0gbGFwcGx5KFggPSBwbG90cywgRlVOID0gZnVuY3Rpb24oeCkgeCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKSArIGd1aWRlcyhjb2xvciA9IGd1aWRlX2xlZ2VuZChucm93ID0gMywgCiMgICAgIGJ5cm93ID0gVFJVRSwgb3ZlcnJpZGUuYWVzID0gbGlzdChzaXplID0gMykpKSkKIyBDb21iaW5lUGxvdHMocGxvdHMpCmBgYCAgCiMjIyMgTGFiZWwgdHJhbnNmZXIKTm93IHdlIGF0dGVtcHQgdG8gdHJhbnNmZXIgdGhlIGNsdXN0ZXIgbGFiZWxzIG9mIHRoZSBTY2llbmNlIGRhdGFzZXQgb250byB0aGUgMTBYIGRhdGFzZXQuCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGluY2x1ZGU9RkFMU0UsIHBhZ2VkLnByaW50PVRSVUV9CmxvYWQoIn4vRG9jdW1lbnRzL1NpbmdsZUNlbGxEYXRhL1NjaWVuY2VkYXRhc2V0L1NjaWVuY2VtYXRyaWNlc2Fubm8uUmRhdGEiKQphbm5vX3NjaWVuY2UkU2FtcGxlIDwtIHJlcCgiU2NpZW5jZSIsbmNvbChlbWF0X3NjaWVuY2UpKQpTY2llbmNlIDwtIENyZWF0ZVNldXJhdE9iamVjdChlbWF0X3NjaWVuY2UsIG1ldGEuZGF0YSA9ICBhbm5vX3NjaWVuY2UsbWluLmNlbGxzID0gMywgbWluLmZlYXR1cmVzID0gMjAwKQpTY2llbmNlIDwtIFNDVHJhbnNmb3JtKFNjaWVuY2UsIG1pbl9jZWxscz0zLHZlcmJvc2UgPSBGQUxTRSkKI29saWdvcy5pbnRlZ3JhdGVkIDwtIFNjaWVuY2UKRGVmYXVsdEFzc2F5KG9saWdvcy5pbnRlZ3JhdGVkKSA8LSAiU0NUIgoKb2xpZ29zLmFuY2hvcnMgPC0gRmluZFRyYW5zZmVyQW5jaG9ycyhyZWZlcmVuY2UgPSBTY2llbmNlLCBxdWVyeSA9b2xpZ29zLmludGVncmF0ZWQsIGRpbXMgPSAxOjMwLHByb2plY3QucXVlcnkgPSBUKSAKcHJlZGljdGlvbnMgPC0gVHJhbnNmZXJEYXRhKGFuY2hvcnNldCA9IG9saWdvcy5hbmNob3JzLCByZWZkYXRhID0gU2NpZW5jZSRjZWxsX2NsYXNzLCBkaW1zID0gMTozMCkKb2xpZ29zLmludGVncmF0ZWQgPC0gQWRkTWV0YURhdGEob2xpZ29zLmludGVncmF0ZWQsIG1ldGFkYXRhID0gcHJlZGljdGlvbnMpCkRpbVBsb3Qob2xpZ29zLmludGVncmF0ZWQsIGdyb3VwLmJ5ID0gYygicHJlZGljdGVkLmlkIiksIGNvbWJpbmUgPSBGQUxTRSkKCmxlbmd0aCh3aGljaChvbGlnb3MuaW50ZWdyYXRlZCRwcmVkaWN0aW9uLnNjb3JlLm1heCA8IDAuMykpCmNvbmZpZGVudGNsdXN0ZXJzIDwtIG9saWdvcy5pbnRlZ3JhdGVkJHByZWRpY3RlZC5pZApjb25maWRlbnRjbHVzdGVyc1t3aGljaChvbGlnb3MuaW50ZWdyYXRlZCRwcmVkaWN0aW9uLnNjb3JlLm1heCA8IDAuMyldIDwtICJOQSIKcHJlZGljdGlvbnMgPC1jYmluZChwcmVkaWN0aW9ucyxjb25maWRlbnRjbHVzdGVycykKb2xpZ29zLmludGVncmF0ZWQgPC0gQWRkTWV0YURhdGEob2xpZ29zLmludGVncmF0ZWQsIG1ldGFkYXRhID0gcHJlZGljdGlvbnMpCmBgYApgYGB7cn0Kb2xpZ29zLmludGVncmF0ZWQgPC0gRmluZE5laWdoYm9ycyhvbGlnb3MuaW50ZWdyYXRlZCwgZGltcyA9IDE6MzApCm9saWdvcy5pbnRlZ3JhdGVkIDwtIEZpbmRDbHVzdGVycyhvbGlnb3MuaW50ZWdyYXRlZCxhbGdvcml0aG0gPSA0LHJlc29sdXRpb24gPSAwLjYpCiMwLjYKYGBgCmBgYHtyfQpvbGlnb3MuaW50ZWdyYXRlZCRwcmVkaWN0ZWQuaWQgPC0gZmFjdG9yKG9saWdvcy5pbnRlZ3JhdGVkJHByZWRpY3RlZC5pZCxsZXZlbHM9YygiT1BDIiwiQ09QIiwiTkZPTDEiLCJNRk9MMSIsIk1GT0wyIiwiTU9MMSIsIk1PTDIiLCJNT0wzIiwiTU9MNCIsIk1PTDUiLCJNT0w2IiwiUFBSIikpCkRpbVBsb3Qob2xpZ29zLmludGVncmF0ZWQsIGdyb3VwLmJ5ID0gYygic2V1cmF0X2NsdXN0ZXJzIiksIGNvbWJpbmUgPSBGQUxTRSkKRGltUGxvdChvbGlnb3MuaW50ZWdyYXRlZCwgZ3JvdXAuYnkgPSBjKCJwcmVkaWN0ZWQuaWQiKSwgY29tYmluZSA9IEZBTFNFKQpEaW1QbG90KG9saWdvcy5pbnRlZ3JhdGVkLCBncm91cC5ieSA9IGMoIlNhbXBsZSIpLCBjb21iaW5lID0gRkFMU0UpCmBgYAoKYGBge3J9CnRhYmxlKG9saWdvcy5pbnRlZ3JhdGVkJFNhbXBsZSxvbGlnb3MuaW50ZWdyYXRlZCRwcmVkaWN0ZWQuaWQpCmBgYAoKYGBge3J9CiNzdWJzZXQgT1BDcwpvbGlnb3MuaW50ZWdyYXRlZEltIDwtIHN1YnNldChvbGlnb3MuaW50ZWdyYXRlZCxzZXVyYXRfY2x1c3RlcnMgJWluJSBjKDEyLDExLDgsOSkpCgoKb2xpZ29zLmludGVncmF0ZWRJbSA8LSBGaW5kVmFyaWFibGVGZWF0dXJlcyhvbGlnb3MuaW50ZWdyYXRlZEltKQpEZWZhdWx0QXNzYXkob2xpZ29zLmludGVncmF0ZWRJbSkgPC0gIlNDVCIKYGBgCgoKI1NwYXRpYWxseSBmaWx0ZXIgZ2VuZXMgd2l0aCBhdXRvIGJvb3RzdHJhcHBpbmcKI1NjcmlwdCB3aWxsIGdpdmUgZXJyb3Igd2hlbiBydW5uaW5nIG91dCBvZiBnZW5lcywgdGhpcyBpcyBub3JtYWwKYGBge3IsIG1lc3NhZ2U9RkFMU0V9Cm5ldHdvcmtFeHByZXNzaW9uRmlsZT1vbGlnb3MuaW50ZWdyYXRlZEltQGFzc2F5cyRTQ1RAc2NhbGUuZGF0YQpleHByX2xpbWl0IDwtIG1pbihuZXR3b3JrRXhwcmVzc2lvbkZpbGUpLTAuMDEKZmVhdHVyZXNlbGVjdGlvbiA8LSByb3cubmFtZXMobmV0d29ya0V4cHJlc3Npb25GaWxlKQpnYygpCmxpYnJhcnkodW1hcCkKdXNlVU1BUCA8LSAiVFJVRSIKVU1BUGRpbSA8LSAzCmFkanVzdGZvcmJpYXMgPC0gIkZBTFNFIgpHZW5lTWFya292TGlzdCA8LSBsaXN0KCkKR2VuZUZpbHRlclByb2dyZXNzaW9uIDwtIGFzLmRhdGEuZnJhbWUoZmVhdHVyZXNlbGVjdGlvbikKcm93Lm5hbWVzKEdlbmVGaWx0ZXJQcm9ncmVzc2lvbikgPC0gZmVhdHVyZXNlbGVjdGlvbgpjb3JlbnVtYmVyIDwtIDQKdXNlbWFnaWMgPC0gIkZBTFNFIgpwY2F0aHJlc2ggPC0gMjAKbmNvbXBHRiA8LSAyMAp3aGVudG9ib290c3RyYXAgPC0gMjAwMDAgI2NlbGxudW1iZXIKaG93bWFueWJvb3RzIDwtIDEwKzEgI2Jvb3RzdHJhcCByZXBldGl0aW9ucwpzYW1wbGVzaXplIDwtIDIwMDAgI2hvdyBtYW55IGNlbGxzIHRvIHRha2UgZm9yIHNhbXBsaW5nCnRocmVzaG9sZCA8LSAzICN1c2UgbWVhbiBzcGF0aWFsIGNvcnJlbGF0aW9uIG9mIGdlbmVzZXQgb2YgdGhlIGZlYXR1cmVzZWxlY3Rpb24gKDEpIG9yIGFmdGVyIHRoZSBmaXJzdCByb3VuZCBvZiBmaWx0ZXJpbmcgKDIpIChtb3JlIHN0cmljdCwgd2hpY2ggaXMgZGVmYXVsdCkgCm5zaW0gPC0gMTAwCm5ucXVhbnQgPC0gMC45OTUgI2xvd2VyIGZvciBzbWFsbCBkYXRhc2V0cywgaGlnaGVyIGZvciBzcGVlZHVwcyBpbiBsYXJnZXIgZGF0YXNldHMKc2V0LnNlZWQoMTIzNCkKdHJpLnRvLnNxdTwtZnVuY3Rpb24oeCkKewpybjwtcm93Lm5hbWVzKHgpCmNuPC1jb2xuYW1lcyh4KQphbjwtdW5pcXVlKGMoY24scm4pKQpteXZhbDwteFshaXMubmEoeCldCm15bWF0PC1tYXRyaXgoMSxucm93PWxlbmd0aChhbiksbmNvbD1sZW5ndGgoYW4pLGRpbW5hbWVzPWxpc3QoYW4sYW4pKQpmb3IoZXh0IGluIDE6bGVuZ3RoKGNuKSkKewogZm9yKGludCBpbiAxOmxlbmd0aChybikpCiB7CiBpZihpcy5uYSh4W3Jvdy5uYW1lcyh4KT09cm5baW50XSxjb2xuYW1lcyh4KT09Y25bZXh0XV0pKSBuZXh0CiBteW1hdFtyb3cubmFtZXMobXltYXQpPT1ybltpbnRdLGNvbG5hbWVzKG15bWF0KT09Y25bZXh0XV08LXhbcm93Lm5hbWVzKHgpPT1ybltpbnRdLGNvbG5hbWVzKHgpPT1jbltleHRdXQogbXltYXRbcm93Lm5hbWVzKG15bWF0KT09Y25bZXh0XSxjb2xuYW1lcyhteW1hdCk9PXJuW2ludF1dPC14W3Jvdy5uYW1lcyh4KT09cm5baW50XSxjb2xuYW1lcyh4KT09Y25bZXh0XV0KIH0KfQpyZXR1cm4obXltYXQpCn0KClNDTjNFZ2VuZXNldCA8LSBmZWF0dXJlc2VsZWN0aW9uCm9yaWdpbmFsbmV0d29ya2RmIDwtIG5ldHdvcmtFeHByZXNzaW9uRmlsZQppZihuY29sKG5ldHdvcmtFeHByZXNzaW9uRmlsZSk+d2hlbnRvYm9vdHN0cmFwKXsKb3JpZ2luYWxuZXR3b3JrZGYgPC0gbmV0d29ya0V4cHJlc3Npb25GaWxlCmJvb3RzdHJhcDwtMQpzYW1wbGU8LTEKbW9yYW5zSXNhbXBsZWQgPC0gYXMuY2hhcmFjdGVyKE5VTEwpCn0KaWYobmNvbChuZXR3b3JrRXhwcmVzc2lvbkZpbGUpPD13aGVudG9ib290c3RyYXApCnsKICBib290c3RyYXA8LWhvd21hbnlib290cy0xCiAgc2FtcGxlPC0wCiAgfQp3aGlsZShib290c3RyYXA8aG93bWFueWJvb3RzKXsKU0NOM0VnZW5lc2V0IDwtIGZlYXR1cmVzZWxlY3Rpb24KaXRlcj0wCm1lYW5Nb3JhbjwtMAppZihzYW1wbGU9PTEpewpuZXR3b3JrRXhwcmVzc2lvbkZpbGUgIDwtIG9yaWdpbmFsbmV0d29ya2RmWyxzYW1wbGUobmNvbChvcmlnaW5hbG5ldHdvcmtkZiksIHNhbXBsZXNpemUpIF0KcHJpbnQocGFzdGUoImJvb3RzdHJhcHBpbmcuLi4iLCJyb3VuZCIsYm9vdHN0cmFwKSkKfQpPbGRHZW5lc2V0IDwtIGFzLmNoYXJhY3RlcihzZXFfbGVuKDEwMDAwMCkpCndoaWxlKGxlbmd0aChPbGRHZW5lc2V0KSA+IChsZW5ndGgoU0NOM0VnZW5lc2V0KSsxMCkgKXsgIyB1bmNvbW1lbnQgdGhpcyBhbmQgYmVsb3cgdG8gZW5hYmxlIGl0ZXJhdGl2ZSBmaWx0ZXJpbmcKIGl0ZXI9aXRlcisxCiAgI2xpYnJhcnkoYW1hcCkKI2xpYnJhcnkoTUFTUykKI2xpYnJhcnkoZGVzdGlueSkKI2xpYnJhcnkoZHB0KQojbGlicmFyeShNYXRyaXgpCiNsaWJyYXJ5KGRpZmZ1c2lvbk1hcCkKcHJpbnQocGFzdGUoIlByZXBhcmluZyBnZW5lZmlsdGVyaW5nIG9uIiwgbGVuZ3RoKFNDTjNFZ2VuZXNldCksICJnZW5lcy4iKSkKI3ByaW50KCJNYWtpbmcgY2VsbGxhbmRzY2FwZS4uLkZpbHRlcmluZyBwb3NzaWJsZSBkdXBsaWNhdGVkIGNlbGxzIGluIG9yaWdpbmFsIGZpbGUiKQojaWYobGVuZ3RoKE9sZEdlbmVzZXQpPT0xMDAwMDApCiN7CiMgIHBjYSA8LSBwcmNvbXAodChsb2cobmV0d29ya0V4cHJlc3Npb25GaWxlW2ZlYXR1cmVzZWxlY3Rpb24sXSsxKSksc2NhbGUuID0gRkFMU0UpCiMgIGNkX2RpZmZ1c2lvbnBsb3QgPC1wY2EkeFssYygxOjMwKV0KIyAgfQoKI2lmKGxlbmd0aChPbGRHZW5lc2V0KSE9MTAwMDAwKXtjZF9kaWZmdXNpb25wbG90IDwtIHQobG9nKG5ldHdvcmtFeHByZXNzaW9uRmlsZVtTQ04zRWdlbmVzZXQsXSsxKSl9CiAgaWYobGVuZ3RoKFNDTjNFZ2VuZXNldCkgPiBwY2F0aHJlc2gpewojbGlicmFyeShyc3ZkKQogICAgcHJpbnQoIk1ha2luZyBjZWxsbGFuZHNjYXBlLi4uUGVyZm9ybWluZyBkaW1lbnNpb25hbGl0eSByZWR1Y3Rpb24iKQojcGNhIDwtIHJwY2EodChsb2cobmV0d29ya0V4cHJlc3Npb25GaWxlW1NDTjNFZ2VuZXNldCxdKzEpKSkKI3BjYSA8LSBwcmNvbXAodChsb2cobmV0d29ya0V4cHJlc3Npb25GaWxlW1NDTjNFZ2VuZXNldCxdKzEpKSxzY2FsZS4gPSBUUlVFKQpwY2EgPC0gcHJjb21wKHQobmV0d29ya0V4cHJlc3Npb25GaWxlW1NDTjNFZ2VuZXNldCxdKSxzY2FsZS4gPSBUUlVFKQppZihsZW5ndGgoU0NOM0VnZW5lc2V0KSA8IDEwMCkge25jb21wR0YgPC0gbGVuZ3RoKFNDTjNFZ2VuZXNldCl9CmNkX2RpZmZ1c2lvbnBsb3QgPC1wY2EkeFssYygxOndoaWNoKGN1bXN1bShwY2Ekc2RldlsxOm5jb21wR0ZdKS9zdW0ocGNhJHNkZXZbMTpuY29tcEdGXSkgPiAwLjgpWzFdKV0KICB9CiAgIGlmKGxlbmd0aChTQ04zRWdlbmVzZXQpIDw9IHBjYXRocmVzaCl7CiNjZF9kaWZmdXNpb25wbG90IDwtdChsb2cobmV0d29ya0V4cHJlc3Npb25GaWxlW1NDTjNFZ2VuZXNldCxdKzEpKQpjZF9kaWZmdXNpb25wbG90IDwtdChuZXR3b3JrRXhwcmVzc2lvbkZpbGVbU0NOM0VnZW5lc2V0LF0pCn0KY2RfZGlmZnVzaW9ucGxvdCA8LSBjZF9kaWZmdXNpb25wbG90WyFkdXBsaWNhdGVkKGNkX2RpZmZ1c2lvbnBsb3QpLF0KcHJpbnQoIk1ha2luZyBjZWxsbGFuZHNjYXBlLi4uTWFraW5nIGRpZmZ1c2lvbm1hcCIpCiMgZ2MoKQojdHMgPC0gVHJhbnNpdGlvbnMoY2RfZGlmZnVzaW9ucGxvdCxrPTIwKQppZih1c2VVTUFQPT1UUlVFKQp7CiAgbGlicmFyeSh1bWFwKQp1bWFwLnNldHRpbmdzIDwtIHVtYXAuZGVmYXVsdHMKdW1hcC5zZXR0aW5ncyRuX25laWdoYm9ycyA8LTEwCnVtYXAuc2V0dGluZ3MkbWluX2Rpc3QgPC0gMAp1bWFwLnNldHRpbmdzJG5fY29tcG9uZW50cyA8LSBVTUFQZGltCnVtYXAuc2V0dGluZ3MkbWV0cmljIDwtICJtYW5oYXR0YW4iCiN1bWFwLnNldHRpbmdzJGxvY2FsX2Nvbm5lY3Rpdml0eSA8LSAwLjAwMDAxCnVtYXAuc2V0dGluZ3Mkbl9lcG9jaHMgPC0gMTAwMAp1bWFwX291dCA8LSB1bWFwKGNkX2RpZmZ1c2lvbnBsb3QsY29uZmlnID0gdW1hcC5zZXR0aW5ncyxtZXRob2Q9InVtYXAtbGVhcm4iKQp9CmlmKHVzZVVNQVAgIT0gVFJVRSl7CmxpYnJhcnkoZGVzdGlueSkKdHMgPC0gRGlmZnVzaW9uTWFwKGNkX2RpZmZ1c2lvbnBsb3QsIHZlcmJvc2UgPSBUUlVFKQp9CmlmKGFkanVzdGZvcmJpYXM9PVRSVUUpewp0ZXN0d2lsY294IDwtIGFzLm1hdHJpeChhcHBseSh0KHRzQGVpZ2VudmVjdG9ycyksMSxmdW5jdGlvbih4KSBhcHBseSh0cmkudG8uc3F1KHBhaXJ3aXNlLndpbGNveC50ZXN0KHgsYmlhc2VkZmFjdG9yLHAuYWRqdXN0Lm1ldGhvZCA9ICJmZHIiKSRwLnZhbHVlKSwyLGZ1bmN0aW9uKHgpIG1pbih4LG5hLnJtPVRSVUUpKSkpCgp0ZXN0d2lsY294ZGlmZmVyZW5jZSA8LSBzY2FsZSh0KHRlc3R3aWxjb3gpKQpiaWFzZWRjb21wb25lbnRzIDwtIHVuaXF1ZSh1bmxpc3QoYXBwbHkodGVzdHdpbGNveGRpZmZlcmVuY2UsMixmdW5jdGlvbih4KSB3aGljaChhYnMoeCkgPiAxLjk2KSkpKQp9CiNHZW5lIGZpbHRlcmluZwpwcmludChwYXN0ZSgiRmlsdGVyaW5nIiwgbGVuZ3RoKFNDTjNFZ2VuZXNldCksICJnZW5lcy4iKSkKT2xkR2VuZXNldCA8LSBTQ04zRWdlbmVzZXQKI05ldHdvcmtEaXN0IDwtIGFzLm1hdHJpeCh0c0B0cmFuc2l0aW9ucykKbGlicmFyeShhbWFwKQpyYW5nZTAxIDwtIGZ1bmN0aW9uKHgpeyh4LW1pbih4KSkvKG1heCh4KS1taW4oeCkpfQppZih1c2VVTUFQPT1UUlVFKQogIHsKICBpZihhZGp1c3Rmb3JiaWFzPT1UUlVFKXsKTmV0d29ya0Rpc3QgPC0gMS1yYW5nZTAxKChhcy5tYXRyaXgoRGlzdCh1bWFwX291dCRsYXlvdXRbLDE6VU1BUGRpbV0sbWV0aG9kID0gIm1hbmhhdHRhbiIsbmJwcm9jID0gOCkpKSl9CmVsc2V7TmV0d29ya0Rpc3QgPC0gMS1yYW5nZTAxKChhcy5tYXRyaXgoRGlzdCh1bWFwX291dCRsYXlvdXRbLDE6VU1BUGRpbV0sbWV0aG9kID0gIm1hbmhhdHRhbiIsbmJwcm9jID0gOCkpKSl9Cn0KaWYodXNlVU1BUCAhPSBUUlVFKXsKaWYoYWRqdXN0Zm9yYmlhcz09VFJVRSl7Ck5ldHdvcmtEaXN0IDwtIDEtcmFuZ2UwMSgoYXMubWF0cml4KERpc3QodHNAZWlnZW52ZWN0b3JzWywtYyhiaWFzZWRjb21wb25lbnRzKV0sbWV0aG9kID0gIm1hbmhhdHRhbiIsbmJwcm9jID0gOCkpKSl9CmVsc2V7TmV0d29ya0Rpc3QgPC0gMS1yYW5nZTAxKChhcy5tYXRyaXgoRGlzdCh0c0BlaWdlbnZlY3RvcnMsbWV0aG9kID0gIm1hbmhhdHRhbiIsbmJwcm9jID0gOCkpKSl9Cn0KI05ldHdvcmtEaXN0IDwtIGRpc3RjZWxscypkaXN0Y2VsbHMyCmNvbG5hbWVzKE5ldHdvcmtEaXN0KSA8LSByb3cubmFtZXMoY2RfZGlmZnVzaW9ucGxvdCkKcm93Lm5hbWVzKE5ldHdvcmtEaXN0KSA8LSByb3cubmFtZXMoY2RfZGlmZnVzaW9ucGxvdCkKZW1hdF9leHByZXNzZWQgPC0gYXBwbHkobmV0d29ya0V4cHJlc3Npb25GaWxlLDEsZnVuY3Rpb24oeCkgYW55ICgoeCkgPmV4cHJfbGltaXQpKQplbWF0X2V4cHJlc3NlZCA8LSBuZXR3b3JrRXhwcmVzc2lvbkZpbGVbZW1hdF9leHByZXNzZWQscm93Lm5hbWVzKE5ldHdvcmtEaXN0KV0KZW1hdF9leHByZXNzZWQgPC0gYXMubWF0cml4KGVtYXRfZXhwcmVzc2VkW2ludGVyc2VjdChyb3cubmFtZXMoZW1hdF9leHByZXNzZWQpLE9sZEdlbmVzZXQpLF0pCmlmKHVzZW1hZ2ljPT0iVFJVRSIpewplbWF0X2V4cHJlc3NlZCA8LSBtYWdpYyh0cyxlbWF0X2V4cHJlc3NlZFsscm93Lm5hbWVzKGNkX2RpZmZ1c2lvbnBsb3QpXSwgcG93ZXIgPSAxLCBrID0gMjAsIG5fZWlncyA9IDIwLCBuX2xvY2FsID0gMTApCn0KI2xpYnJhcnkoZG9QYXJhbGxlbCkKI2xpYnJhcnkoZm9yZWFjaCkKbGlicmFyeShwYXJhbGxlbCkKbGlicmFyeShzcGRlcCkKY29yZXMgPC0gY29yZW51bWJlcgojIGNsIDwtIG1ha2VDbHVzdGVyKGNvcmVzKSAgCiMgcmVnaXN0ZXJEb1BhcmFsbGVsKGNsKSAgClNDTjNFZ2VuZWZpbHRlciA8LSBtYXRyaXgobnJvdz1ucm93KGVtYXRfZXhwcmVzc2VkKSxuY29sID0gMykgCmNlbGxuYW1lcyA8LSBjb2xuYW1lcyhlbWF0X2V4cHJlc3NlZCkKIyBsaWJyYXJ5KE1hdHJpeCkKIyBOZXR3b3JrRGlzdHNwYXJzZSA8LSBNYXRyaXgoTmV0d29ya0Rpc3QsIHNwYXJzZSA9IFRSVUUpCnByaW50KCJNYWtpbmcgY2VsbGxhbmRzY2FwZS4uLkdlbmVyYXRpbmcgU3BhdGlhbCBXZWlnaHRzIE1hdHJpeC4uLiIpCmk9MQpOZXR3b3JrRGlzdDIgPC0gbWF0cml4KG5yb3c9bnJvdyhOZXR3b3JrRGlzdCksbmNvbD1uY29sKE5ldHdvcmtEaXN0KSkKICBmb3IoaSBpbiAxOm5jb2woTmV0d29ya0Rpc3QyKSkKICAgIHsKICB4IDwtTmV0d29ya0Rpc3RbLGldIAogeFt4IDwgYXMubnVtZXJpYyhxdWFudGlsZSh4LG5ucXVhbnQpKV0gPC0gMAogTmV0d29ya0Rpc3QyWyxpXSA8LSB4CiAgfQpOZXR3b3JrRGlzdCA8LSBOZXR3b3JrRGlzdDIKcm0oTmV0d29ya0Rpc3QyKQpjb2xuYW1lcyhOZXR3b3JrRGlzdCkgPC0gcm93Lm5hbWVzKGNkX2RpZmZ1c2lvbnBsb3QpCnJvdy5uYW1lcyhOZXR3b3JrRGlzdCkgPC0gcm93Lm5hbWVzKGNkX2RpZmZ1c2lvbnBsb3QpCnNwYXRpYWwud2VpZ2h0cyA8LSBtYXQybGlzdHcoTmV0d29ya0Rpc3RbXSkKcHJpbnQoIk1ha2luZyBjZWxsbGFuZHNjYXBlLi4uR2VuZXJhdGluZyBTcGF0aWFsIFdlaWdodHMgTWF0cml4Li4uZG9uZSIpCm51bWJzaW0gPC0gbnNpbQppPTEKdGVzdCA8LSBhcy5kYXRhLmZyYW1lKG1jbGFwcGx5KDE6bnJvdyhlbWF0X2V4cHJlc3NlZCksIGZ1bmN0aW9uKGkpIHsKICAgcmV0dXJuKG0gPC0gYyh1bmxpc3QobW9yYW4ubWMoZW1hdF9leHByZXNzZWRbaSxdLHNwYXRpYWwud2VpZ2h0cyxuc2ltPW51bWJzaW0semVyby5wb2xpY3kgPSBUUlVFKSlbMTozXSxyb3cubmFtZXMoZW1hdF9leHByZXNzZWQpW2ldKSkKfSwgbWMuY29yZXM9Y29yZW51bWJlcikpCgoKU0NOM0VnZW5lZmlsdGVyIDwtIHQodGVzdCkKU0NOM0VnZW5lZmlsdGVyIDwtIFNDTjNFZ2VuZWZpbHRlclshIGFwcGx5KFNDTjNFZ2VuZWZpbHRlciwxLGZ1bmN0aW9uKHgpIGFueSh4PT0iTmFOIikpLF0Kcm93Lm5hbWVzKFNDTjNFZ2VuZWZpbHRlcikgPC0gU0NOM0VnZW5lZmlsdGVyWyw0XQpTQ04zRWdlbmVmaWx0ZXJfY2xlYW4gPC0gYXMuZGF0YS5mcmFtZShTQ04zRWdlbmVmaWx0ZXJbY29tcGxldGUuY2FzZXMoU0NOM0VnZW5lZmlsdGVyKSxjKDE6NCldKQogciA8LSByb3cubmFtZXMoU0NOM0VnZW5lZmlsdGVyX2NsZWFuKQpTQ04zRWdlbmVmaWx0ZXJfY2xlYW4gPC0gYXBwbHkoU0NOM0VnZW5lZmlsdGVyX2NsZWFuLDIsZnVuY3Rpb24oeCkgYXMubnVtZXJpYyh4KSkKcm93Lm5hbWVzKFNDTjNFZ2VuZWZpbHRlcl9jbGVhbikgPC0gcgpTQ04zRWdlbmVzZXQgPC0gcm93Lm5hbWVzKHN1YnNldChTQ04zRWdlbmVmaWx0ZXJfY2xlYW4sU0NOM0VnZW5lZmlsdGVyX2NsZWFuWywzXSA8PSAwLjAxKSkKSFNFZ2VuZXMgPC0gcm93Lm5hbWVzKHN1YnNldChTQ04zRWdlbmVmaWx0ZXJfY2xlYW4sU0NOM0VnZW5lZmlsdGVyX2NsZWFuWywzXSA8PSAwLjAxICYgU0NOM0VnZW5lZmlsdGVyX2NsZWFuWywxXSA+PSBhcy5udW1lcmljKHF1YW50aWxlKFNDTjNFZ2VuZWZpbHRlcl9jbGVhbltTQ04zRWdlbmVzZXQsMV0sMC4xKSkpKQpTQ04zRWdlbmVzZXQgPC0gcm93Lm5hbWVzKHN1YnNldChTQ04zRWdlbmVmaWx0ZXJfY2xlYW4sU0NOM0VnZW5lZmlsdGVyX2NsZWFuWywzXSA8PSAwLjAxICYgU0NOM0VnZW5lZmlsdGVyX2NsZWFuWywxXSA+PSBhcy5udW1lcmljKHF1YW50aWxlKFNDTjNFZ2VuZWZpbHRlcl9jbGVhbltTQ04zRWdlbmVzZXQsMV0sMC4xKSkpKQpHZW5lRmlsdGVyUHJvZ3Jlc3Npb24gPC0gY2JpbmQoR2VuZUZpbHRlclByb2dyZXNzaW9uW1NDTjNFZ2VuZXNldCxdLFNDTjNFZ2VuZWZpbHRlcl9jbGVhbltTQ04zRWdlbmVzZXQsMV0pCmlmKGl0ZXI8PXRocmVzaG9sZCl7bWVhbk1vcmFuPC1tZWFuKGFicyhTQ04zRWdlbmVmaWx0ZXJfY2xlYW5bLDFdKSxuYS5ybT1UUlVFKX0KR2VuZU1hcmtvdkxpc3QgPC0gYyhsaXN0KEdlbmVNYXJrb3Y9U0NOM0VnZW5lc2V0KSxHZW5lTWFya292TGlzdCkKfSAjdW5jb21tZW50IHRoaXMgdG8gZW5hYmxlIGl0ZXJhdGl2ZSBmaWx0ZXJpbmcKYm9vdHN0cmFwIDwtIGJvb3RzdHJhcCsxCmlmKHNhbXBsZT09MSl7Cm1vcmFuc0lzYW1wbGVkIDwtIGMobW9yYW5zSXNhbXBsZWQsU0NOM0VnZW5lc2V0KQp9Cn0KbmV0d29ya0V4cHJlc3Npb25GaWxlIDwtIG9yaWdpbmFsbmV0d29ya2RmCkdlbmVNYXJrb3YgPC0gU0NOM0VnZW5lc2V0CmlmKHNhbXBsZT09MSl7R2VuZU1hcmtvdiA8LSB1bmlxdWUobW9yYW5zSXNhbXBsZWQpfQojTDFnZW5lc2V0IDwtIEdlbmVNYXJrb3YKcm0oZW1hdF9leHByZXNzZWQsTmV0d29ya0Rpc3QsU0NOM0VnZW5lZmlsdGVyLHRlc3QsY2VsbG5hbWVzLGNsLGNvcmVzLGksaXRlcixtZWFuTW9yYW4sbnVtYnNpbSxPbGRHZW5lc2V0LHIsU0NOM0VnZW5lc2V0LHNwYXRpYWwud2VpZ2h0cykKTDFnZW5lc2V0IDwtIEdlbmVNYXJrb3YKcm0oZW1hdF9leHByZXNzZWQsTmV0d29ya0Rpc3QsU0NOM0VnZW5lZmlsdGVyLHRlc3QsY2VsbG5hbWVzLGNsLGNvcmVzLGksaXRlcixtZWFuTW9yYW4sbnVtYnNpbSxPbGRHZW5lc2V0LHIsU0NOM0VnZW5lc2V0LHNwYXRpYWwud2VpZ2h0cykKCiAgcHJpbnQoIk1ha2luZyBmaW5hbCBjZWxsbGFuZHNjYXBlLi4uRmlsdGVyaW5nIHBvc3NpYmxlIGR1cGxpY2F0ZWQgY2VsbHMgaW4gb3JpZ2luYWwgZmlsZSIpCiAgIGlmKGxlbmd0aChHZW5lTWFya292KSA+IHBjYXRocmVzaCl7CnBjYSA8LSBwcmNvbXAodChsb2cobmV0d29ya0V4cHJlc3Npb25GaWxlW0dlbmVNYXJrb3YsXSsxKSksc2NhbGUuID0gRkFMU0UpCgppZihsZW5ndGgoR2VuZU1hcmtvdikgPCAxMDApIHtuY29tcEdGIDwtIGxlbmd0aChHZW5lTWFya292KX0KY2RfZGlmZnVzaW9ucGxvdCA8LXBjYSR4WyxjKDE6d2hpY2goY3Vtc3VtKHBjYSRzZGV2WzE6bmNvbXBHRl0pL3N1bShwY2Ekc2RldlsxOm5jb21wR0ZdKSA+IDAuOClbMV0pXQogIH0KICAgaWYobGVuZ3RoKEdlbmVNYXJrb3YpIDw9IHBjYXRocmVzaCl7CmNkX2RpZmZ1c2lvbnBsb3QgPC10KGxvZyhuZXR3b3JrRXhwcmVzc2lvbkZpbGVbR2VuZU1hcmtvdixdKzEpKQp9CmNkX2RpZmZ1c2lvbnBsb3QgPC0gY2RfZGlmZnVzaW9ucGxvdFshZHVwbGljYXRlZChjZF9kaWZmdXNpb25wbG90KSxdCmlmKHVzZVVNQVAgIT0gVFJVRSl7CnByaW50KCJNYWtpbmcgZmluYWwgY2VsbGxhbmRzY2FwZS4uLk1ha2luZyBkaWZmdXNpb25tYXAuIFRoaXMgY2FuIHRha2UgYSB3aGlsZS4uLiIpCmxpYnJhcnkoZGVzdGlueSkKdHMgPC0gRGlmZnVzaW9uTWFwKGNkX2RpZmZ1c2lvbnBsb3QsIHZlcmJvc2UgPSBUUlVFKQp9CiAgaWYodXNlbWFnaWM9PSJUUlVFIil7CiAgICBuZXR3b3JrRXhwcmVzc2lvbkZpbGVwcmVtYWdpYyA8LSAgbmV0d29ya0V4cHJlc3Npb25GaWxlCm5ldHdvcmtFeHByZXNzaW9uRmlsZSA8LSBtYWdpYyh0cyxuZXR3b3JrRXhwcmVzc2lvbkZpbGVbLHJvdy5uYW1lcyhjZF9kaWZmdXNpb25wbG90KV0sIHBvd2VyID0gMSwgayA9IDIwLCBuX2VpZ3MgPSAyMCwgbl9sb2NhbCA9IDEwKQogIH0KaWYodXNlVU1BUD09VFJVRSkKewogIHByaW50KCJNYWtpbmcgZmluYWwgY2VsbGxhbmRzY2FwZS4uLk1ha2luZyBVTUFQLiBUaGlzIGNhbiB0YWtlIGEgd2hpbGUuLi4iKQp1bWFwLnNldHRpbmdzIDwtIHVtYXAuZGVmYXVsdHMKdW1hcC5zZXR0aW5ncyRuX25laWdoYm9ycyA8LTQKdW1hcC5zZXR0aW5ncyRtaW5fZGlzdCA8LSAwCnVtYXAuc2V0dGluZ3Mkbl9jb21wb25lbnRzIDwtVU1BUGRpbQp1bWFwLnNldHRpbmdzJG1ldHJpYyA8LSAibWFuaGF0dGFuIgojdW1hcC5zZXR0aW5ncyRsb2NhbF9jb25uZWN0aXZpdHkgPC0gMC4wMDAwMQp1bWFwLnNldHRpbmdzJG5fZXBvY2hzIDwtIDEwMDAKdW1hcF9vdXQgPC0gdW1hcChjZF9kaWZmdXNpb25wbG90LGNvbmZpZyA9IHVtYXAuc2V0dGluZ3MsbWV0aG9kPSJ1bWFwLWxlYXJuIikKfQpwcmludCgiRG9uZS4iKQppZihhZGp1c3Rmb3JiaWFzPT1UUlVFKXsKdGVzdHdpbGNveCA8LSBhcy5tYXRyaXgoYXBwbHkodCh0c0BlaWdlbnZlY3RvcnMpLDEsZnVuY3Rpb24oeCkgYXBwbHkodHJpLnRvLnNxdShwYWlyd2lzZS53aWxjb3gudGVzdCh4LGJpYXNlZGZhY3RvcixwLmFkanVzdC5tZXRob2QgPSAiZmRyIikkcC52YWx1ZSksMixmdW5jdGlvbih4KSBtaW4oeCxuYS5ybT1UUlVFKSkpKQoKdGVzdHdpbGNveGRpZmZlcmVuY2UgPC0gc2NhbGUodCh0ZXN0d2lsY294KSkKYmlhc2VkY29tcG9uZW50c3dpbGNveCA8LSB1bmlxdWUodW5saXN0KGFwcGx5KHRlc3R3aWxjb3gsMixmdW5jdGlvbih4KSB3aGljaChhYnMoeCkgPT0wKSkpKQpiaWFzZWRjb21wb25lbnRzIDwtIHVuaXF1ZSh1bmxpc3QoYXBwbHkodGVzdHdpbGNveGRpZmZlcmVuY2UsMixmdW5jdGlvbih4KSB3aGljaChhYnMoeCkgPiAxLjk2KSkpKQp9CmBgYApgYGB7cn0KbGVuZ3RoX2l0ZXJhdGlvbnMgPC0gIHVubGlzdChsYXBwbHkoR2VuZU1hcmtvdkxpc3QsZnVuY3Rpb24oeCkgbGVuZ3RoKHgpKSkKcGxvdChzZXFfYWxvbmcobGVuZ3RoX2l0ZXJhdGlvbnMpLGxlbmd0aF9pdGVyYXRpb25zKQpgYGAKYGBge3J9CnBjYXRzbmUgPC0gcHJjb21wKHQoYXMubWF0cml4KG5ldHdvcmtFeHByZXNzaW9uRmlsZVt1bmxpc3QoR2VuZU1hcmtvdkxpc3RbMjZdKSxdKSksc2NhbGUuID0gVFJVRSkKcGNhIDwtIHBjYXRzbmUKcGxvdChsb2cocGNhdHNuZSRzZGV2WzE6NTBdKSxwY2ggPSAyMCwKeGxhYiA9ICdQcmluY2lwYWwgY29tcG9uZW50JywgeWxhYiA9ICdzZGV2JykKbmNvbXBMMXRzbmUgPC0gd2hpY2goY3Vtc3VtKHBjYSRzZGV2WzE6NTBdKS9zdW0ocGNhJHNkZXZbMTo1MF0pID4gMC42KVsxXQp0c25lIDwtIHQocGNhJHhbLGMoMTpuY29tcEwxdHNuZSldKSAKCm51bWJlcl9vZl9jbHVzdGVycyA8LSA4Ck5ldHdvcmtEaXN0IDwtIERpc3QodCh0c25lKSxtZXRob2QgPSAibWFuaGF0dGFuIixuYnByb2MgPSA4KQpHZW5lTWFya292X2hjIDwtIGhjbHVzdChOZXR3b3JrRGlzdCwgbWV0aG9kID0gIndhcmQuRDIiKQpHTWNsdXN0ZXIgPC0gcmJpbmQoZ3JvdXBzID0gY3V0cmVlKEdlbmVNYXJrb3ZfaGMsIGs9bnVtYmVyX29mX2NsdXN0ZXJzKSkKbmFtZXMoR01jbHVzdGVyKSA8LSBjb2xuYW1lcyhuZXR3b3JrRXhwcmVzc2lvbkZpbGUpCm9saWdvcy5pbnRlZ3JhdGVkSW0kc2V1cmF0X2NsdXN0ZXJzIDwtIGFzLmZhY3RvcihHTWNsdXN0ZXIpCkdlbmVNYXJrb3YgIDwtIHVubGlzdChHZW5lTWFya292TGlzdFsyN10pCmBgYApgYGB7cn0KRGVmYXVsdEFzc2F5KG9saWdvcy5pbnRlZ3JhdGVkSW0pIDwtICJTQ1QiCm9saWdvcy5pbnRlZ3JhdGVkSW0gPC0gUnVuUENBKG9saWdvcy5pbnRlZ3JhdGVkSW0sIHZlcmJvc2UgPSBGQUxTRSxmZWF0dXJlcyA9IEdlbmVNYXJrb3YpI25wY3MgPSAyMCkKRWxib3dQbG90KG9saWdvcy5pbnRlZ3JhdGVkSW0pCmBgYApgYGB7cn0Kb2xpZ29zLmludGVncmF0ZWRJbSA8LSBSdW5VTUFQKG9saWdvcy5pbnRlZ3JhdGVkSW0sIGRpbXMgPSAxOjEwKQojb2xpZ29zLmludGVncmF0ZWRTY2llbmNlIDwtIFJ1blRTTkUob2xpZ29zLmludGVncmF0ZWRTY2llbmNlLCBkaW1zID0gMToxMCkKcGxvdHMgPC0gRGltUGxvdChvbGlnb3MuaW50ZWdyYXRlZEltLCBncm91cC5ieSA9IGMoInNldXJhdF9jbHVzdGVycyIpLCBjb21iaW5lID0gRkFMU0UpCnBsb3RzIDwtIGxhcHBseShYID0gcGxvdHMsIEZVTiA9IGZ1bmN0aW9uKHgpIHggKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAidG9wIikgKyBndWlkZXMoY29sb3IgPSBndWlkZV9sZWdlbmQobnJvdyA9IDMsIAogICAgYnlyb3cgPSBUUlVFLCBvdmVycmlkZS5hZXMgPSBsaXN0KHNpemUgPSAzKSkpKQpDb21iaW5lUGxvdHMocGxvdHMpCnBsb3RzIDwtIERpbVBsb3Qob2xpZ29zLmludGVncmF0ZWRJbSwgZ3JvdXAuYnkgPSBjKCJTYW1wbGUiKSwgY29tYmluZSA9IEZBTFNFKQpwbG90cyA8LSBsYXBwbHkoWCA9IHBsb3RzLCBGVU4gPSBmdW5jdGlvbih4KSB4ICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpICsgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfbGVnZW5kKG5yb3cgPSAzLCAKICAgIGJ5cm93ID0gVFJVRSwgb3ZlcnJpZGUuYWVzID0gbGlzdChzaXplID0gMykpKSkKQ29tYmluZVBsb3RzKHBsb3RzKQpgYGAgIApgYGB7ciBpbmNsdWRlPUZBTFNFfQojIGZpbmQgbWFya2VycyBmb3IgZXZlcnkgY2x1c3RlciBjb21wYXJlZCB0byBhbGwgcmVtYWluaW5nIGNlbGxzLCByZXBvcnQgb25seSB0aGUgcG9zaXRpdmUgb25lcwpJZGVudHMob2xpZ29zLmludGVncmF0ZWRJbSkgPC0gInNldXJhdF9jbHVzdGVycyIKbGlicmFyeShkcGx5cikKb2xpZ29zLmludGVncmF0ZWQubWFya2Vyc0ltIDwtIEZpbmRBbGxNYXJrZXJzKG9saWdvcy5pbnRlZ3JhdGVkSW0sIG9ubHkucG9zID0gVFJVRSwgbWluLnBjdCA9IDAuMjUsIGxvZ2ZjLnRocmVzaG9sZCA9IDAuMjUpCmBgYApgYGB7cn0Kb2xpZ29zLmludGVncmF0ZWQubWFya2Vyc0ltICU+JSBncm91cF9ieShjbHVzdGVyKSAlPiUgdG9wX24obiA9IDIsIHd0ID0gYXZnX2xvZ0ZDKQpgYGAKYGBge3IgZmlnLndpZHRoPTEwfQpsaWJyYXJ5KHZpcmlkaXMpCkRlZmF1bHRBc3NheShvbGlnb3MuaW50ZWdyYXRlZEltKSA8LSAiU0NUIgp0b3AxMCA8LSBvbGlnb3MuaW50ZWdyYXRlZC5tYXJrZXJzSW0gJT4lIGdyb3VwX2J5KGNsdXN0ZXIpICU+JSB0b3BfbihuID0gMTAsIHd0ID0gYXZnX2xvZ0ZDKQojIERvSGVhdG1hcChvbGlnb3MuaW50ZWdyYXRlZEltLCBmZWF0dXJlcyA9IGludGVyc2VjdChvbGlnb3MuaW50ZWdyYXRlZC5tYXJrZXJzSW0kZ2VuZSxHZW5lTWFya292KSxkaXNwLm1heD03LCkgKyBOb0xlZ2VuZCgpICtzY2FsZV9maWxsX3ZpcmlkaXMoKQoKRG9IZWF0bWFwKG9saWdvcy5pbnRlZ3JhdGVkSW0sIGZlYXR1cmVzID1pbnRlcnNlY3Qob2xpZ29zLmludGVncmF0ZWQubWFya2Vyc0ltJGdlbmUsdW5pcXVlKGModW5saXN0KEdlbmVNYXJrb3ZMaXN0WzVdKSx0b3AxMCRnZW5lKSkpLGRpc3AubWF4PTMpICsgTm9MZWdlbmQoKSArc2NhbGVfZmlsbF92aXJpZGlzKCkKYGBgCmBgYHtyfQpub25PTCA8LSBjb2xuYW1lcyhvbGlnb3MuaW50ZWdyYXRlZEltKVtvbGlnb3MuaW50ZWdyYXRlZEltQG1ldGEuZGF0YSRzZXVyYXRfY2x1c3RlcnMgJWluJSBjKDM6Niw4KV0Kb2xpZ29zLmludGVncmF0ZWQgPC0gb2xpZ29zLmludGVncmF0ZWRbLCEgY29sbmFtZXMob2xpZ29zLmludGVncmF0ZWQpICVpbiUgbm9uT0xdCmRhdGEgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZShvbGlnb3MuaW50ZWdyYXRlZCRTYW1wbGUsZHJvcGxldmVscyhhcy5mYWN0b3Iob2xpZ29zLmludGVncmF0ZWQkcHJlZGljdGVkLmlkKSkpKQpjb2xuYW1lcyhkYXRhKSA8LSBjKCJDb25kaXRpb24iLCJDbHVzdGVyIiwiRnJlcSIpCmxpYnJhcnkocGx5cikKZGF0YSRDbHVzdGVyICA8LSBmYWN0b3IoZGF0YSRDbHVzdGVyLGxldmVscz1jKCJPUEMiLCJDT1AiLCJORk9MMSIsIk1GT0wxIiwiTUZPTDIiLCJNT0wxIiwiTU9MMiIsIk1PTDMiLCJNT0w0IiwiTU9MNSIsIk1PTDYiLCJQUFIiKSkKZGF0YSA8LSBkYXRhW3doaWNoKGRhdGEkQ2x1c3RlciAlaW4lIGMoIk1GT0wxIiwiTUZPTDIiLCJNT0wxIiwiTU9MMiIsIk1PTDMiLCJNT0w0IiwiTU9MNSIsIk1PTDYiKSksXQojZGF0YSRDbHVzdGVyICA8LSBmYWN0b3IoZGF0YSRDbHVzdGVyLGxldmVscz1jKCJNRk9MMSIsIk1GT0wyIiwiTU9MMSIsIk1PTDIiLCJNT0wzIiwiTU9MNCIsIk1PTDUiLCJNT0w2IikpCmxpYnJhcnkocmVzaGFwZTIpCmRhdGFjYXN0ZWQgPC0gZGNhc3QoZGF0YSxDbHVzdGVyIH4gQ29uZGl0aW9uKQpjYWxjX2NwbSA8LWZ1bmN0aW9uIChleHByX21hdCkgCnsKICAgIG5vcm1fZmFjdG9yIDwtIGNvbFN1bXMoZXhwcl9tYXQpCiAgICByZXR1cm4odCh0KGV4cHJfbWF0KS9ub3JtX2ZhY3RvcikpCn0KZGF0YWNhc3RlZFssMjo0XSA8LSBjYWxjX2NwbShkYXRhY2FzdGVkWywyOjRdKQpkYXRhIDwtIG1lbHQoZGF0YWNhc3RlZCkKY29sbmFtZXMoZGF0YSkgPC0gYygiQ29uZGl0aW9uIiwiQ2x1c3RlciIsIkZyZXEiKQojZGF0YSRDbHVzdGVyICA8LSByZXZhbHVlKGFzLmZhY3RvcihkYXRhJENsdXN0ZXIpLGMoIlBQUiI9IlZMTUMiKSkKIyBTdGFja2VkICsgcGVyY2VudApnZ3Bsb3QoZGF0YSwgYWVzKGZpbGw9Q29uZGl0aW9uLCB5PUZyZXEsIHg9Q2x1c3RlcikpICsgCiAgICBnZW9tX2Jhcihwb3NpdGlvbj0iZmlsbCIsIHN0YXQ9ImlkZW50aXR5IikKZ2dwbG90KGRhdGEsIGFlcyhmaWxsPUNsdXN0ZXIsIHk9RnJlcSwgeD1Db25kaXRpb24pKSArIAogICAgZ2VvbV9iYXIoIHN0YXQ9ImlkZW50aXR5IikKCnJvdy5uYW1lcyhkYXRhY2FzdGVkKSA8LSBkYXRhY2FzdGVkWywxXQpkYXRhY2FzdGVkIDwtIGRhdGFjYXN0ZWRbLDI6NF0qMTAwCmRhdGFtZWx0ZWQgPC0gbWVsdCh0KGRhdGFjYXN0ZWQpKQoKZ2dwbG90KGRhdGFtZWx0ZWQsIGFlcyh5ID0gdmFsdWUsIHggPSBWYXIyKSkgKyAjIE1vdmUgeSBhbmQgeCBoZXJlIHNvIHRoYW4gdGhleSBjYW4gYmUgdXNlZCBpbiBzdGF0XyoKICAgIGdlb21fZG90cGxvdChhZXMoZmlsbCA9IFZhcjEpLCAgICMgVXNlIGZpbGwgPSBTcGVjaWVzIGhlcmUgbm90IGluIGdncGxvdCgpCiAgICAgICAgICAgICAgICAgYmluYXhpcyA9ICJ5IiwgICAgICAgICAjIHdoaWNoIGF4aXMgdG8gYmluIGFsb25nCiAgICAgICAgICAgICAgICAgYmlud2lkdGggPSAxLCAgICAgICAgIyBNaW5pbWFsIGRpZmZlcmVuY2UgY29uc2lkZXJlZCBkaWZmZWVyZW50CiAgICAgICAgICAgICAgICAgc3RhY2tkaXIgPSAiY2VudGVyIiwKICAgICAgICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2ppdHRlcigwLjIpIyBDZW50ZXJlZAogICAgICAgICAgICAgICAgICkgKyAgIyBzY2FsZV95X2xvZzEwKCkgKyAKICAgIHN0YXRfc3VtbWFyeShmdW4ueSA9IG1lYW4sIGZ1bi55bWluID0gbWVhbiwgZnVuLnltYXggPSBtZWFuLAogICAgICAgICAgICAgICAgIGdlb20gPSAiY3Jvc3NiYXIiLCB3aWR0aCA9IDAuNSxmYXR0ZW4gPSAwLjAxKSArIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUpKQpgYGAKYGBge3IgZmlnLmhlaWdodD0zLCBmaWcud2lkdGg9M30KbGlicmFyeShoZWF0bWFwMykKbGlicmFyeSh2aXJpZGlzKQpkYXRhIDwtIGFzLmRhdGEuZnJhbWUodGFibGUob2xpZ29zLmludGVncmF0ZWQkU2FtcGxlLGRyb3BsZXZlbHMoYXMuZmFjdG9yKG9saWdvcy5pbnRlZ3JhdGVkJHByZWRpY3RlZC5pZCkpKSkKY29sbmFtZXMoZGF0YSkgPC0gYygiQ29uZGl0aW9uIiwiQ2x1c3RlciIsIkZyZXEiKQpsaWJyYXJ5KHBseXIpCmRhdGEkQ2x1c3RlciAgPC0gZmFjdG9yKGRhdGEkQ2x1c3RlcixsZXZlbHM9YygiT1BDIiwiQ09QIiwiTkZPTDEiLCJNRk9MMSIsIk1GT0wyIiwiTU9MMSIsIk1PTDIiLCJNT0wzIiwiTU9MNCIsIk1PTDUiLCJNT0w2IiwiUFBSIikpCmRhdGEgPC0gZGF0YVt3aGljaChkYXRhJENsdXN0ZXIgJWluJSBjKCJNRk9MMSIsIk1GT0wyIiwiTU9MMSIsIk1PTDIiLCJNT0wzIiwiTU9MNCIsIk1PTDUiLCJNT0w2IikpLF0KI2RhdGEkQ2x1c3RlciAgPC0gZmFjdG9yKGRhdGEkQ2x1c3RlcixsZXZlbHM9YygiTUZPTDEiLCJNRk9MMiIsIk1PTDEiLCJNT0wyIiwiTU9MMyIsIk1PTDQiLCJNT0w1IiwiTU9MNiIpKQpsaWJyYXJ5KHJlc2hhcGUyKQpkYXRhY2FzdGVkIDwtIGRjYXN0KGRhdGEsQ2x1c3RlciB+IENvbmRpdGlvbikKY2FsY19jcG0gPC1mdW5jdGlvbiAoZXhwcl9tYXQpIAp7CiAgICBub3JtX2ZhY3RvciA8LSBjb2xTdW1zKGV4cHJfbWF0KQogICAgcmV0dXJuKHQodChleHByX21hdCkvbm9ybV9mYWN0b3IpKQp9CmRhdGFjYXN0ZWRbLDI6NF0gPC0gY2FsY19jcG0oZGF0YWNhc3RlZFssMjo0XSkqMTAwCnJvdy5uYW1lcyhkYXRhY2FzdGVkKSA8LSBkYXRhY2FzdGVkWywxXQpkYXRhY2FzdGVkIDwtIGRhdGFjYXN0ZWRbLDI6NF0KY29tcGFyaXNvbiA8LWRhdGFjYXN0ZWQtYXBwbHkoZGF0YWNhc3RlZCwxLGZ1bmN0aW9uKHgpIG1lYW4oeCkpCmNvbXBhcmlzb24gPC1kYXRhY2FzdGVkLWRhdGFjYXN0ZWRbLDFdCmhlYXRtYXAzKGNvbXBhcmlzb25bcmV2KHJvdy5uYW1lcyhjb21wYXJpc29uKSksXSwgUm93diA9IE5BICwgQ29sdiA9IE5BICxzY2FsZSA9ICJub25lIixzeW1tID0gRiwgbWV0aG9kID0gIndhcmQuRDIiLGNvbD12aXJpZGlzKDEwMDApLGJhbGFuY2VDb2xvciA9RixjZXhSb3cgPSAxLGNleENvbCA9IDEsbWFyZ2lucyA9IGMoMTAsIDEwKSkKbGlicmFyeShSQ29sb3JCcmV3ZXIpCmhlYXRtYXAzKGNvbXBhcmlzb25bcmV2KHJvdy5uYW1lcyhjb21wYXJpc29uKSksXSwgUm93diA9IE5BICwgQ29sdiA9IE5BICxzY2FsZSA9ICJub25lIixzeW1tID0gRiwgbWV0aG9kID0gIndhcmQuRDIiLGNvbD1yZXYoY29sb3JSYW1wUGFsZXR0ZShicmV3ZXIucGFsKDEwMjQsIlJkQnUiKSkoMTAyNCkpLGJhbGFuY2VDb2xvciA9VCxjZXhSb3cgPSAxLGNleENvbCA9IDEsbWFyZ2lucyA9IGMoMTAsIDEwKSkKICByZWxhdGlvbnNoaXByYXRpbyA8LSBjb3IodChjb21wYXJpc29uKSxtZXRob2Q9InBlYXJzb24iKQpoZWF0bWFwMyhyZWxhdGlvbnNoaXByYXRpb1tyZXYocm93Lm5hbWVzKGNvbXBhcmlzb24pKSxdLCBSb3d2ID0gTlVMTCAsIENvbHYgPSBOVUxMICxzY2FsZSA9ICJub25lIixzeW1tID0gRiwgbWV0aG9kID0gIndhcmQuRDIiLGNvbD1jb2xvclJhbXBQYWxldHRlKGMoImxpbWVncmVlbiIsImJsYWNrIiwKImZpcmVicmljazMiKSkoMTAyNCksYmFsYW5jZUNvbG9yID1GLGNleFJvdyA9IDIsY2V4Q29sID0gMixtYXJnaW5zID0gYygxMCwgMTApKQpgYGAKYGBge3J9CmJhcnBsb3QodGFibGUob2xpZ29zLmludGVncmF0ZWQkU2FtcGxlLG9saWdvcy5pbnRlZ3JhdGVkJHByZWRpY3RlZC5pZCkpCmRhdGEgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZShvbGlnb3MuaW50ZWdyYXRlZCRTYW1wbGUsb2xpZ29zLmludGVncmF0ZWQkcHJlZGljdGVkLmlkKSkKY29sbmFtZXMoZGF0YSkgPC0gYygiQ29uZGl0aW9uIiwiQ2x1c3RlciIsIkZyZXEiKQpsaWJyYXJ5KHBseXIpCmRhdGEkQ2x1c3RlciAgPC0gZmFjdG9yKGRhdGEkQ2x1c3RlcixsZXZlbHM9YygiT1BDIiwiQ09QIiwiTkZPTDEiLCJNRk9MMSIsIk1GT0wyIiwiTU9MMSIsIk1PTDIiLCJNT0wzIiwiTU9MNCIsIk1PTDUiLCJNT0w2IiwiUFBSIikpCmRhdGEkQ2x1c3RlciAgPC0gcmV2YWx1ZShhcy5mYWN0b3IoZGF0YSRDbHVzdGVyKSxjKCJQUFIiPSJWTE1DIikpCmRhdGFJU19DVFJMIDwtIGRhdGFbd2hpY2goZGF0YSRDb25kaXRpb24gJWluJSBjKCJJUyIsIkNUUkwiKSksXQpkYXRhV0RfQ1RSTCA8LSBkYXRhW3doaWNoKGRhdGEkQ29uZGl0aW9uICVpbiUgYygiV0QiLCJDVFJMIikpLF0KZGF0YUlTX1dEIDwtIGRhdGFbd2hpY2goZGF0YSRDb25kaXRpb24gJWluJSBjKCJJUyIsIldEIikpLF0KIyBTdGFja2VkICsgcGVyY2VudApnZ3Bsb3QoZGF0YSwgYWVzKGZpbGw9Q29uZGl0aW9uLCB5PUZyZXEsIHg9Q2x1c3RlcikpICsgCiAgICBnZW9tX2Jhcihwb3NpdGlvbj0iZmlsbCIsIHN0YXQ9ImlkZW50aXR5IikKZ2dwbG90KGRhdGFJU19DVFJMLCBhZXMoZmlsbD1Db25kaXRpb24sIHk9RnJlcSwgeD1DbHVzdGVyKSkgKyAKICAgIGdlb21fYmFyKHBvc2l0aW9uPSJmaWxsIiwgc3RhdD0iaWRlbnRpdHkiKQpnZ3Bsb3QoZGF0YVdEX0NUUkwsIGFlcyhmaWxsPUNvbmRpdGlvbiwgeT1GcmVxLCB4PUNsdXN0ZXIpKSArIAogICAgZ2VvbV9iYXIocG9zaXRpb249ImZpbGwiLCBzdGF0PSJpZGVudGl0eSIpCmdncGxvdChkYXRhSVNfV0QsIGFlcyhmaWxsPUNvbmRpdGlvbiwgeT1GcmVxLCB4PUNsdXN0ZXIpKSArIAogICAgZ2VvbV9iYXIocG9zaXRpb249ImZpbGwiLCBzdGF0PSJpZGVudGl0eSIpCmBgYApgYGB7cn0KI2ZvciBNVD0xMCUKb2xpZ29zLmludGVncmF0ZWRPTCA8LSBzdWJzZXQob2xpZ29zLmludGVncmF0ZWQsc2V1cmF0X2NsdXN0ZXJzICVpbiUgYygxOjYsOCw5KSkKIyAjc3Vic2V0IG5vbk9MbGluZWFnZSBjZWxscwojIG5vbk9MIDwtIGNvbG5hbWVzKG9saWdvcy5pbnRlZ3JhdGVkSW0pW29saWdvcy5pbnRlZ3JhdGVkSW1AbWV0YS5kYXRhJHNldXJhdF9jbHVzdGVycyAlaW4lIGMoMzo2LDgpXQojIG9saWdvcy5pbnRlZ3JhdGVkT0wgPC0gb2xpZ29zLmludGVncmF0ZWRPTFssISBjb2xuYW1lcyhvbGlnb3MuaW50ZWdyYXRlZE9MKSAlaW4lIG5vbk9MXQojbm9uLWludGVncmF0ZWQKI29saWdvcy5pbnRlZ3JhdGVkT0wgPC0gc3Vic2V0KG9saWdvcy5pbnRlZ3JhdGVkLHNldXJhdF9jbHVzdGVycyAlaW4lIGMoMTo2LDEwKSkKI2ZvciBNVD01JQojb2xpZ29zLmludGVncmF0ZWRPTCA8LSBzdWJzZXQob2xpZ29zLmludGVncmF0ZWQsc2V1cmF0X2NsdXN0ZXJzICVpbiUgYygxOjUpKQpEZWZhdWx0QXNzYXkob2xpZ29zLmludGVncmF0ZWQpIDwtICJTQ1QiCiMgb2xpZ29zLmludGVncmF0ZWRPTCA8LSBSdW5QQ0Eob2xpZ29zLmludGVncmF0ZWRPTCwgdmVyYm9zZSA9IEZBTFNFLGZlYXR1cmVzPWMoIk9wYWxpbiIsIlB0Z2RzIiwiQXBvZSIsIlMxMDBiIiwiQXBvZCIsIkxhbXAxIiwiRm9zIiwiU2VwcDEiKSxucGNzPTUsYXBwcm94PUZBTFNFKQpvbGlnb3MuaW50ZWdyYXRlZE9MIDwtIFJ1blBDQShvbGlnb3MuaW50ZWdyYXRlZE9MLCB2ZXJib3NlID0gRkFMU0UpIyxmZWF0dXJlcz1HZW5lTWFya292KQpFbGJvd1Bsb3Qob2xpZ29zLmludGVncmF0ZWRPTCkKYGBgCmBgYHtyfQpvbGlnb3MuaW50ZWdyYXRlZE9MIDwtIFJ1blVNQVAob2xpZ29zLmludGVncmF0ZWRPTCwgZGltcyA9IDE6MzApCiNvbGlnb3MuaW50ZWdyYXRlZFNjaWVuY2UgPC0gUnVuVFNORShvbGlnb3MuaW50ZWdyYXRlZFNjaWVuY2UsIGRpbXMgPSAxOjEwKQpwbG90cyA8LSBEaW1QbG90KG9saWdvcy5pbnRlZ3JhdGVkT0wsIGdyb3VwLmJ5ID0gYygic2V1cmF0X2NsdXN0ZXJzIiksIGNvbWJpbmUgPSBGQUxTRSkKcGxvdHMgPC0gbGFwcGx5KFggPSBwbG90cywgRlVOID0gZnVuY3Rpb24oeCkgeCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKSArIGd1aWRlcyhjb2xvciA9IGd1aWRlX2xlZ2VuZChucm93ID0gMywgCiAgICBieXJvdyA9IFRSVUUsIG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZSA9IDMpKSkpCkNvbWJpbmVQbG90cyhwbG90cykKcGxvdHMgPC0gRGltUGxvdChvbGlnb3MuaW50ZWdyYXRlZE9MLCBncm91cC5ieSA9IGMoIlNhbXBsZSIpLCBjb21iaW5lID0gRkFMU0UpCnBsb3RzIDwtIGxhcHBseShYID0gcGxvdHMsIEZVTiA9IGZ1bmN0aW9uKHgpIHggKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAidG9wIikgKyBndWlkZXMoY29sb3IgPSBndWlkZV9sZWdlbmQobnJvdyA9IDMsIAogICAgYnlyb3cgPSBUUlVFLCBvdmVycmlkZS5hZXMgPSBsaXN0KHNpemUgPSAzKSkpKQpDb21iaW5lUGxvdHMocGxvdHMpCgpEaW1QbG90KG9saWdvcy5pbnRlZ3JhdGVkT0wsIGdyb3VwLmJ5ID0gYygiU2FtcGxlIiksIGNvbWJpbmUgPSBGQUxTRSkKRGltUGxvdChvbGlnb3MuaW50ZWdyYXRlZE9MLCBncm91cC5ieSA9IGMoInNldXJhdF9jbHVzdGVycyIpLCBjb21iaW5lID0gRkFMU0UpCkRpbVBsb3Qob2xpZ29zLmludGVncmF0ZWRPTCwgZ3JvdXAuYnkgPSBjKCJwcmVkaWN0ZWQuaWQiKSwgY29tYmluZSA9IEZBTFNFLGxhYmVsPVRSVUUpCmBgYCAgCmBgYHtyIGluY2x1ZGU9RkFMU0V9CkRlZmF1bHRBc3NheShvbGlnb3MuaW50ZWdyYXRlZE9MKSA8LSAiU0NUIgojIGZpbmQgbWFya2VycyBmb3IgZXZlcnkgY2x1c3RlciBjb21wYXJlZCB0byBhbGwgcmVtYWluaW5nIGNlbGxzLCByZXBvcnQgb25seSB0aGUgcG9zaXRpdmUgb25lcwpJZGVudHMob2xpZ29zLmludGVncmF0ZWRPTCkgPC0gInByZWRpY3RlZC5pZCIKbGlicmFyeShkcGx5cikKb2xpZ29zLmludGVncmF0ZWQubWFya2Vyc09MIDwtIEZpbmRBbGxNYXJrZXJzKG9saWdvcy5pbnRlZ3JhdGVkT0wsIG9ubHkucG9zID0gVFJVRSwgbWluLnBjdCA9IDAuMjUsIGxvZ2ZjLnRocmVzaG9sZCA9IDAuMSkKYGBgCmBgYHtyfQpvbGlnb3MuaW50ZWdyYXRlZC5tYXJrZXJzT0wgJT4lIGdyb3VwX2J5KGNsdXN0ZXIpICU+JSB0b3BfbihuID0gMiwgd3QgPSBhdmdfbG9nRkMpCmBgYAoKYGBge3IgZmlnLndpZHRoPTEwfQpEZWZhdWx0QXNzYXkob2xpZ29zLmludGVncmF0ZWRPTCkgPC0gIlNDVCIKIyBOb3JtYWxpemUgUk5BIGRhdGEgZm9yIHZpc3VhbGl6YXRpb24gcHVycG9zZXMKI29saWdvcy5pbnRlZ3JhdGVkIDwtIE5vcm1hbGl6ZURhdGEob2xpZ29zLmludGVncmF0ZWQsIHZlcmJvc2UgPSBGQUxTRSkKRmVhdHVyZVBsb3Qob2xpZ29zLmludGVncmF0ZWRPTCwgYygiUGRnZnJhIiwgIlB0cHJ6MSIsIkJtcDQiLCJJdHByMiIsICJFZ3IxIiwiRWdyMiIsICJGb3MiLCJLbGs2IiwgIkhvcHgiLCAiUHRnZHMiLCJJbDMzIiwiTWJwIiwiQ2Q3NCIsIlNlcnBpbmEzbiIpLHB0LnNpemUgPSAxKQoKRmVhdHVyZVBsb3Qob2xpZ29zLmludGVncmF0ZWRPTCwgYygiT3BhbGluIiwiUHRnZHMiLCJBcG9lIiwiUzEwMGIiLCJBcG9kIiwiTGFtcDEiLCJGb3MiLCJTZXBwMSIsIktsazYiLCJIb3B4IikscHQuc2l6ZSA9IDEpCkRlZmF1bHRBc3NheShvbGlnb3MuaW50ZWdyYXRlZCkgPC0gIlNDVCIKYGBgCgpgYGB7cn0KbGlicmFyeShnZ3JlcGVsKQpEZWZhdWx0QXNzYXkob2xpZ29zLmludGVncmF0ZWRPTCkgPC0gIlNDVCIKSWRlbnRzKG9saWdvcy5pbnRlZ3JhdGVkT0wpIDwtICJTYW1wbGUiCm9saWdvcy5pbnRlZ3JhdGVkLnNhbXBsZWRpZmZJUyA8LSBGaW5kTWFya2VycyhvbGlnb3MuaW50ZWdyYXRlZE9MLCBpZGVudC4xID0gIklTIiwgaWRlbnQuMiA9ICJDVFJMIiwgdmVyYm9zZSA9IEZBTFNFLGxvZ2ZjLnRocmVzaG9sZCA9IDAsbWluLnBjdD0wKQojaGVhZChvbGlnb3MuaW50ZWdyYXRlZC5zYW1wbGVkaWZmQWxsUk5BLCBuID0gNTApCmRpZmZtYXRyaXggPC0gb2xpZ29zLmludGVncmF0ZWQuc2FtcGxlZGlmZklTCmRpZmZtYXRyaXgkbG9ncF92YWwgPC0gLWxvZzEwKGRpZmZtYXRyaXgkcF92YWxfYWRqKQpnZ3Bsb3QoZGlmZm1hdHJpeCxhZXMoYXZnX2xvZ0ZDLHk9bG9ncF92YWwsbGFiZWw9cm93Lm5hbWVzKGRpZmZtYXRyaXgpKSkrIGdlb21fcG9pbnQoc2l6ZT0wLjUpKyBnZW9tX3RleHRfcmVwZWwoZGF0YT1zdWJzZXQoZGlmZm1hdHJpeCwgcF92YWxfYWRqIDwgMC4wMSAmIGFicyhhdmdfbG9nRkMpID4gMC43KSwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbD1yb3cubmFtZXMoc3Vic2V0KGRpZmZtYXRyaXgsIHBfdmFsX2FkaiA8IDAuMDEgJiBhYnMoYXZnX2xvZ0ZDKSA+IDAuNykpKSt4bGFiKCJsb2cyX0ZDIikgKyB5bGFiKCItbG9nMTBfcC12YWx1ZV9hZGoiKSArIGdlb21faGxpbmUoeWludGVyY2VwdD0tbG9nMTAoMC4wMSksbGluZXR5cGU9ImRhc2hlZCIsc2l6ZT0wLjUpIAoKb2xpZ29zLmludGVncmF0ZWQuc2FtcGxlZGlmZldEIDwtIEZpbmRNYXJrZXJzKG9saWdvcy5pbnRlZ3JhdGVkT0wsIGlkZW50LjEgPSAiV0QiLCBpZGVudC4yID0gYygiQ1RSTCIpLCB2ZXJib3NlID0gRkFMU0UsbG9nZmMudGhyZXNob2xkID0gMCxtaW4ucGN0PTApCiNoZWFkKG9saWdvcy5pbnRlZ3JhdGVkLnNhbXBsZWRpZmZBbGxSTkEsIG4gPSA1MCkKZGlmZm1hdHJpeCA8LSBvbGlnb3MuaW50ZWdyYXRlZC5zYW1wbGVkaWZmV0QKZGlmZm1hdHJpeCRsb2dwX3ZhbCA8LSAtbG9nMTAoZGlmZm1hdHJpeCRwX3ZhbF9hZGopCmdncGxvdChkaWZmbWF0cml4LGFlcyhhdmdfbG9nRkMseT1sb2dwX3ZhbCxsYWJlbD1yb3cubmFtZXMoZGlmZm1hdHJpeCkpKSsgZ2VvbV9wb2ludChzaXplPTAuNSkrIGdlb21fdGV4dF9yZXBlbChkYXRhPXN1YnNldChkaWZmbWF0cml4LCBwX3ZhbF9hZGogPCAwLjAxICYgYWJzKGF2Z19sb2dGQykgPiAwLjM1KSwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbD1yb3cubmFtZXMoc3Vic2V0KGRpZmZtYXRyaXgsIHBfdmFsX2FkaiA8IDAuMDEgJiBhYnMoYXZnX2xvZ0ZDKSA+IDAuMzUpKSkreGxhYigibG9nMl9GQyIpICsgeWxhYigiLWxvZzEwX3AtdmFsdWVfYWRqIikgKyBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9LWxvZzEwKDAuMDEpLGxpbmV0eXBlPSJkYXNoZWQiLHNpemU9MC41KSAKCm9saWdvcy5pbnRlZ3JhdGVkLnNhbXBsZWRpZmZJU1dEIDwtIEZpbmRNYXJrZXJzKG9saWdvcy5pbnRlZ3JhdGVkT0wsIGlkZW50LjEgPSAiSVMiLCBpZGVudC4yID0gIldEIiwgdmVyYm9zZSA9IEZBTFNFLGxvZ2ZjLnRocmVzaG9sZCA9IDAsbWluLnBjdD0wKQojaGVhZChvbGlnb3MuaW50ZWdyYXRlZC5zYW1wbGVkaWZmQWxsUk5BLCBuID0gNTApCmRpZmZtYXRyaXggPC0gb2xpZ29zLmludGVncmF0ZWQuc2FtcGxlZGlmZklTV0QKZGlmZm1hdHJpeCRsb2dwX3ZhbCA8LSAtbG9nMTAoZGlmZm1hdHJpeCRwX3ZhbF9hZGopCmdncGxvdChkaWZmbWF0cml4LGFlcyhhdmdfbG9nRkMseT1sb2dwX3ZhbCxsYWJlbD1yb3cubmFtZXMoZGlmZm1hdHJpeCkpKSsgZ2VvbV9wb2ludChzaXplPTAuNSkrIGdlb21fdGV4dF9yZXBlbChkYXRhPXN1YnNldChkaWZmbWF0cml4LCBwX3ZhbF9hZGogPCAwLjAxICYgYWJzKGF2Z19sb2dGQykgPiAwLjg1KSwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbD1yb3cubmFtZXMoc3Vic2V0KGRpZmZtYXRyaXgsIHBfdmFsX2FkaiA8IDAuMDEgJiBhYnMoYXZnX2xvZ0ZDKSA+IDAuODUpKSkreGxhYigibG9nMl9GQyIpICsgeWxhYigiLWxvZzEwX3AtdmFsdWVfYWRqIikgKyBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9LWxvZzEwKDAuMDEpLGxpbmV0eXBlPSJkYXNoZWQiLHNpemU9MC41KSAKCm9saWdvcy5pbnRlZ3JhdGVkLnNhbXBsZWRpZmZJU1dEdnNDTlRSTCA8LSBGaW5kTWFya2VycyhvbGlnb3MuaW50ZWdyYXRlZE9MLCBpZGVudC4xID0gYygiSVMiLCJXRCIpLCBpZGVudC4yID0gIkNUUkwiLCB2ZXJib3NlID0gRkFMU0UsbG9nZmMudGhyZXNob2xkID0gMCxtaW4ucGN0PTApCiNoZWFkKG9saWdvcy5pbnRlZ3JhdGVkLnNhbXBsZWRpZmZBbGxSTkEsIG4gPSA1MCkKZGlmZm1hdHJpeCA8LSBvbGlnb3MuaW50ZWdyYXRlZC5zYW1wbGVkaWZmSVNXRHZzQ05UUkwKZGlmZm1hdHJpeCRsb2dwX3ZhbCA8LSAtbG9nMTAoZGlmZm1hdHJpeCRwX3ZhbF9hZGopCmdncGxvdChkaWZmbWF0cml4LGFlcyhhdmdfbG9nRkMseT1sb2dwX3ZhbCxsYWJlbD1yb3cubmFtZXMoZGlmZm1hdHJpeCkpKSsgZ2VvbV9wb2ludChzaXplPTAuNSkrIGdlb21fdGV4dF9yZXBlbChkYXRhPXN1YnNldChkaWZmbWF0cml4LCBwX3ZhbF9hZGogPCAwLjAxICYgYWJzKGF2Z19sb2dGQykgPiAwLjg1KSwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbD1yb3cubmFtZXMoc3Vic2V0KGRpZmZtYXRyaXgsIHBfdmFsX2FkaiA8IDAuMDEgJiBhYnMoYXZnX2xvZ0ZDKSA+IDAuODUpKSkreGxhYigibG9nMl9GQyIpICsgeWxhYigiLWxvZzEwX3AtdmFsdWVfYWRqIikgKyBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9LWxvZzEwKDAuMDEpLGxpbmV0eXBlPSJkYXNoZWQiLHNpemU9MC41KSAKRGVmYXVsdEFzc2F5KG9saWdvcy5pbnRlZ3JhdGVkKSA8LSAiU0NUIgpgYGAKYGBge3J9CkRpZmZNYXRyaXggPC0gbGlzdCgpCgpkaWZmbWF0cml4bmFtZXMgPC0gYygib2xpZ29zLmludGVncmF0ZWQuc2FtcGxlZGlmZklTV0QiLAogICAgICAgICAgICAgICAgICAgICJvbGlnb3MuaW50ZWdyYXRlZC5zYW1wbGVkaWZmSVNXRHZzQ05UUkwiKQogICAgICAgICAgICAgICAgICAgICAKCmRvLmNhbGwoaGVhZCxhcy5saXN0KGFzLm5hbWUoZGlmZm1hdHJpeG5hbWVzWzFdKSkpCmBgYApgYGB7cn0KRGlmZk1hdHJpeCA8LSBsaXN0KCkKCmRpZmZtYXRyaXhuYW1lcyA8LSBjKCJvbGlnb3MuaW50ZWdyYXRlZC5zYW1wbGVkaWZmSVNXRCIsCiAgICAgICAgICAgICAgICAgICAgIm9saWdvcy5pbnRlZ3JhdGVkLnNhbXBsZWRpZmZJU1dEdnNDTlRSTCIpCiAgICAgICAgICAgICAgICAgICAgIAoKZG8uY2FsbChoZWFkLGFzLmxpc3QoYXMubmFtZShkaWZmbWF0cml4bmFtZXNbMV0pKSkKYGBgCmBgYHtyfQpsaWJyYXJ5KGNsdXN0ZXJQcm9maWxlcikKI0NvbnZlcnQgdG8gZ2VuY29kZSB1c2luZyBiaW9tYXJ0CmxpYnJhcnkoYmlvbWFSdCkKbGlzdE1hcnRzKCkKZW5zZW1ibCA9IHVzZU1hcnQoImVuc2VtYmwiLGRhdGFzZXQ9Im1tdXNjdWx1c19nZW5lX2Vuc2VtYmwiKQpsaXN0RGF0YXNldHMoZW5zZW1ibCkKYXR0cmlidXRlcyA9IGxpc3RBdHRyaWJ1dGVzKGVuc2VtYmwpCkJpb21hcnRfZ2VuY29kZV9lbnNlbWJsODRfYmlvdHlwZXMgPC0gZ2V0Qk0oYXR0cmlidXRlcz1jKCJtZ2lfc3ltYm9sIiwiZW5zZW1ibF9nZW5lX2lkIiwiZW50cmV6Z2VuZV9pZCIsImdlbmVfYmlvdHlwZSIpLCBmaWx0ZXJzID0gIiIsIHZhbHVlcyA9ICIiLCBlbnNlbWJsKQpCaW9tYXJ0X2dlbmNvZGVfZW5zZW1ibDg0X2Jpb3R5cGVzWywgJ2dlbmVfYmlvdHlwZSddIDwtIGFzLmZhY3RvcihCaW9tYXJ0X2dlbmNvZGVfZW5zZW1ibDg0X2Jpb3R5cGVzWywnZ2VuZV9iaW90eXBlJ10pCiNGaWx0ZXIgZm9yIG9ubHkgb3VyIGdlbmVzCiBCaW90eXBlX0FsbF9kYXRhc2V0IDwtIHN1YnNldChCaW9tYXJ0X2dlbmNvZGVfZW5zZW1ibDg0X2Jpb3R5cGVzLCBtZ2lfc3ltYm9sICVpbiUgb2xpZ29zLmludGVncmF0ZWRAYXNzYXlzJFNDVEB2YXIuZmVhdHVyZXMpCmVudHJleklEIDwtICBzdWJzZXQoQmlvdHlwZV9BbGxfZGF0YXNldCwgQmlvdHlwZV9BbGxfZGF0YXNldCRtZ2lfc3ltYm9sICVpbiUgb2xpZ29zLmludGVncmF0ZWRAYXNzYXlzJFNDVEB2YXIuZmVhdHVyZXMpCmBgYApgYGB7cn0KIyBpZiAoIXJlcXVpcmVOYW1lc3BhY2UoIkJpb2NNYW5hZ2VyIiwgcXVpZXRseSA9IFRSVUUpKQojICAgICBpbnN0YWxsLnBhY2thZ2VzKCJCaW9jTWFuYWdlciIpCiMgCiMgQmlvY01hbmFnZXI6Omluc3RhbGwoInJlYWN0b21lLmRiIikKbGlicmFyeShSZWFjdG9tZVBBKQpsaWJyYXJ5KG9yZy5NbS5lZy5kYikKUmVhY3RvbWVUZXJtcyA8LSBsaXN0KCkKaT0xCiNVUApwdmFsYWRqIDwtIDAuMDEKbG9nZmMgPC0gMC4yNQpmb3IoaSBpbiAxOmxlbmd0aChkaWZmbWF0cml4bmFtZXMpKXsKZGlmZm1hdHJpeCA8LSBkby5jYWxsKCJhcy5kYXRhLmZyYW1lIixhcy5saXN0KGFzLm5hbWUoZGlmZm1hdHJpeG5hbWVzW2ldKSkpCmRpZmZtYXRyaXggPC0gc3Vic2V0KGRpZmZtYXRyaXgsIHBfdmFsX2FkaiA8IHB2YWxhZGogJiBhdmdfbG9nRkMgPiBsb2dmYykKc2lnZ2VuZXMgPC0gaGVhZChyb3cubmFtZXMoZGlmZm1hdHJpeCksNTApCmVudHJlem1hdGNoZWQgPC0gZW50cmV6SURbZW50cmV6SUQkbWdpX3N5bWJvbCAlaW4lIHNpZ2dlbmVzLF0KI2VudHJleklEIDwtIGVudHJleklEWyEgYXBwbHkoZW50cmV6SURbLGMoMSwzKV0sIDEsZnVuY3Rpb24gKHgpIGFueU5BKHgpKSxdCmFsbExMSURzIDwtIGVudHJlem1hdGNoZWQkZW50cmV6Z2VuZQptb2R1bGVzUmVhY3RvbWUgPC0gZW5yaWNoUGF0aHdheShnZW5lPWFsbExMSURzLG9yZ2FuaXNtPSJtb3VzZSIscHZhbHVlQ3V0b2ZmPTAuMDEscXZhbHVlQ3V0b2ZmID0gMC4zLHBBZGp1c3RNZXRob2QgPSAibm9uZSIsIHJlYWRhYmxlPVQpClJlYWN0b21lVGVybXNbW2ldXSA8LSBtb2R1bGVzUmVhY3RvbWUKaGVhZChhcy5kYXRhLmZyYW1lKG1vZHVsZXNSZWFjdG9tZSkpCnByaW50KGkpCn0KUmVhY3RvbWVUZXJtc1t3aGljaChsYXBwbHkoUmVhY3RvbWVUZXJtcyxmdW5jdGlvbih4KSBpcy5udWxsKHgpKT09VFJVRSldIDwtICJOb19HZW5lcyIKCiNBZGQgRE9XTiAKcHZhbGFkaiA8LSAwLjAxCmxvZ2ZjIDwtIC0wLjI1Cm9mZnNldCA8LSBsZW5ndGgoUmVhY3RvbWVUZXJtcykKZm9yKGkgaW4gMTpsZW5ndGgoZGlmZm1hdHJpeG5hbWVzKSl7CiAgaT1pK29mZnNldApkaWZmbWF0cml4IDwtIGRvLmNhbGwoImFzLmRhdGEuZnJhbWUiLGFzLmxpc3QoYXMubmFtZShkaWZmbWF0cml4bmFtZXNbaS1vZmZzZXRdKSkpCmRpZmZtYXRyaXggPC0gc3Vic2V0KGRpZmZtYXRyaXgsIHBfdmFsX2FkaiA8IHB2YWxhZGogJiBhdmdfbG9nRkMgPCBsb2dmYykKc2lnZ2VuZXMgPC0gaGVhZChyb3cubmFtZXMoZGlmZm1hdHJpeCksNTApCmVudHJlem1hdGNoZWQgPC0gZW50cmV6SURbZW50cmV6SUQkbWdpX3N5bWJvbCAlaW4lIHNpZ2dlbmVzLF0KI2VudHJleklEIDwtIGVudHJleklEWyEgYXBwbHkoZW50cmV6SURbLGMoMSwzKV0sIDEsZnVuY3Rpb24gKHgpIGFueU5BKHgpKSxdCmFsbExMSURzIDwtIGVudHJlem1hdGNoZWQkZW50cmV6Z2VuZQptb2R1bGVzUmVhY3RvbWUgPC0gZW5yaWNoUGF0aHdheShnZW5lPWFsbExMSURzLG9yZ2FuaXNtPSJtb3VzZSIscHZhbHVlQ3V0b2ZmPTAuMDEscXZhbHVlQ3V0b2ZmID0gMC4zLHBBZGp1c3RNZXRob2QgPSAibm9uZSIsIHJlYWRhYmxlPVQpClJlYWN0b21lVGVybXNbW2ldXSA8LSBtb2R1bGVzUmVhY3RvbWUKaGVhZChhcy5kYXRhLmZyYW1lKG1vZHVsZXNSZWFjdG9tZSkpCnByaW50KGkpCn0KUmVhY3RvbWVUZXJtc1t3aGljaChsYXBwbHkoUmVhY3RvbWVUZXJtcyxmdW5jdGlvbih4KSBpcy5udWxsKHgpKT09VFJVRSldIDwtICJOb19HZW5lcyIKYGBgCgpgYGB7cn0KVXBwZXJfZGlmZiA8LSBzdWJzZXQob2xpZ29zLmludGVncmF0ZWQuc2FtcGxlZGlmZklTV0QsIHBfdmFsX2FkaiA8IDAuMDEgJiBhYnMoYXZnX2xvZ0ZDKSA+IDApCkxvd2VyX2RpZmYgPC0gc3Vic2V0KG9saWdvcy5pbnRlZ3JhdGVkLnNhbXBsZWRpZmZJU1dEdnNDTlRSTCwgcF92YWxfYWRqIDwgMC4wMSAmIGFicyhhdmdfbG9nRkMpID4gMCkKQWxsZGlmZmdlbmVzSGV0TU9MNSA8LSBpbnRlcnNlY3QoaW50ZXJzZWN0KHJvdy5uYW1lcyhvbGlnb3MuaW50ZWdyYXRlZC5zYW1wbGVkaWZmSVNXRCkscm93Lm5hbWVzKG9saWdvcy5pbnRlZ3JhdGVkLnNhbXBsZWRpZmZJU1dEdnNDTlRSTCkpLHVuaXF1ZShjKHJvdy5uYW1lcyhVcHBlcl9kaWZmKSxyb3cubmFtZXMoTG93ZXJfZGlmZikpKSkKc3Vic2V0MiA8LSBvbGlnb3MuaW50ZWdyYXRlZC5zYW1wbGVkaWZmSVNXRFtBbGxkaWZmZ2VuZXNIZXRNT0w1LF0Kc3Vic2V0MyA8LSBvbGlnb3MuaW50ZWdyYXRlZC5zYW1wbGVkaWZmSVNXRHZzQ05UUkxbQWxsZGlmZmdlbmVzSGV0TU9MNSxdCnN1YnNldE1PTDUgPC0gY2JpbmQoc3Vic2V0MixzdWJzZXQzKQpjb2xuYW1lcyhzdWJzZXRNT0w1KSA8LSBtYWtlLnVuaXF1ZShjb2xuYW1lcyhzdWJzZXRNT0w1KSkKZGlmZm1hdHJpeCA8LSBzdWJzZXRNT0w1CmRpZmZtYXRyaXgkbG9nX3BfdmFsIDwtIC1sb2cxMChkaWZmbWF0cml4JHBfdmFsX2FkaikKcTk1cGdlbmVzMSA8LSByb3cubmFtZXMoZGlmZm1hdHJpeFt3aGljaChkaWZmbWF0cml4JGxvZ19wX3ZhbCA+PSBxdWFudGlsZShkaWZmbWF0cml4JGxvZ19wX3ZhbCwwKSksXSkKZGlmZm1hdHJpeCRsb2dfcF92YWwuMSA8LSAtbG9nMTAoZGlmZm1hdHJpeCRwX3ZhbF9hZGouMSkKcTk1cGdlbmVzMiA8LSByb3cubmFtZXMoZGlmZm1hdHJpeFt3aGljaChkaWZmbWF0cml4JGxvZ19wX3ZhbC4xID49IHF1YW50aWxlKGRpZmZtYXRyaXgkbG9nX3BfdmFsLjEsMCkpLF0pCnE5NXBnZW5lcyA8LSB1bmlxdWUoYyhxOTVwZ2VuZXMxLHE5NXBnZW5lczIpKQpkaWZmbWF0cml4IDwtIGRpZmZtYXRyaXhbcTk1cGdlbmVzLF0KZGlmZm1hdHJpeCRhdmdfbG9nRkNbaXMuaW5maW5pdGUoZGlmZm1hdHJpeCRhdmdfbG9nRkMpXSA8LSBtYXgoZGlmZm1hdHJpeCRhdmdfbG9nRkNbIWlzLmluZmluaXRlKGRpZmZtYXRyaXgkYXZnX2xvZ0ZDKV0pCmRpZmZtYXRyaXgkYXZnX2xvZ0ZDLjFbaXMuaW5maW5pdGUoZGlmZm1hdHJpeCRhdmdfbG9nRkMuMSldIDwtIG1heChkaWZmbWF0cml4JGF2Z19sb2dGQy4xWyFpcy5pbmZpbml0ZShkaWZmbWF0cml4JGF2Z19sb2dGQy4xKV0pCiNkaWZmbWF0cml4JGF2Z19sb2dGQy4xIDwtIDIqZGlmZm1hdHJpeCRhdmdfbG9nRkMuMQpkaWZmbWF0cml4JGNvbWJwIDwtIC1sb2cxMChkaWZmbWF0cml4JHBfdmFsX2FkaipkaWZmbWF0cml4JHBfdmFsX2Fkai4xKQpkaWZmbWF0cml4JG1heHAgPC0gYXBwbHkoY2JpbmQoZGlmZm1hdHJpeCRsb2dfcF92YWwsZGlmZm1hdHJpeCRsb2dfcF92YWwuMSksMSxmdW5jdGlvbih4KSBtYXgoeCkpCmRpZmZtYXRyaXgkbWlucCA8LSBhcHBseShjYmluZChkaWZmbWF0cml4JHBfdmFsX2FkaixkaWZmbWF0cml4JHBfdmFsX2Fkai4xKSwxLGZ1bmN0aW9uKHgpIG1pbih4KSkKZGlmZm1hdHJpeCRtYXhwW2lzLmluZmluaXRlKGRpZmZtYXRyaXgkbWF4cCldIDwtIG1heChkaWZmbWF0cml4JG1heHBbIWlzLmluZmluaXRlKGRpZmZtYXRyaXgkbWF4cCldKQpkaWZmbWF0cml4JG1heEZDIDwtIGFwcGx5KGNiaW5kKGRpZmZtYXRyaXgkYXZnX2xvZ0ZDLGRpZmZtYXRyaXgkYXZnX2xvZ0ZDLjEpLDEsZnVuY3Rpb24oeCkgbWF4KGFicyh4KSkpIApkaWZmbWF0cml4JEdlbmVzIDwtIGZhY3Rvcihyb3cubmFtZXMoZGlmZm1hdHJpeCksbGV2ZWxzPXJvdy5uYW1lcyhkaWZmbWF0cml4KSkKCmdncGxvdChkaWZmbWF0cml4LGFlcyhhdmdfbG9nRkMseT1hdmdfbG9nRkMuMSxjb2xvdXI9bWF4cCxsYWJlbD1yb3cubmFtZXMoZGlmZm1hdHJpeCkpKSsgZ2VvbV9wb2ludChzaXplPWRpZmZtYXRyaXgkbWF4cC8xMDApICsgc2NhbGVfY29sb3VyX3ZpcmlkaXNfYyhkaXJlY3Rpb24gPSArMSxvcHRpb24gPSJ2aXJpZGlzIiApICsgZ2VvbV9obGluZSh5aW50ZXJjZXB0PSAwLGxpbmV0eXBlPSJkYXNoZWQiLHNpemU9MC4xLGNvbG9yPSJjeWFuIikrCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PSAwLjI1LGxpbmV0eXBlPSJkYXNoZWQiLHNpemU9MC4xLGNvbG9yPSJncmV5IixhbHBoYT0wLjUpKwogIGdlb21faGxpbmUoeWludGVyY2VwdD0gLTAuMjUsbGluZXR5cGU9ImRhc2hlZCIsc2l6ZT0wLjEsY29sb3I9ImdyZXkiLGFscGhhPTAuNSkrCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0PSAwLGxpbmV0eXBlPSJkYXNoZWQiLHNpemU9MC4xLGNvbG9yPSJjeWFuIikrCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0PSAwLjI1LGxpbmV0eXBlPSJkYXNoZWQiLHNpemU9MC4xLGNvbG9yPSJncmV5IixhbHBoYT0wLjUpKwogIGdlb21fdmxpbmUoeGludGVyY2VwdD0gLTAuMjUsbGluZXR5cGU9ImRhc2hlZCIsc2l6ZT0wLjEsY29sb3I9ImdyZXkiLGFscGhhPTAuNSkrCiAgZ2VvbV90ZXh0X3JlcGVsKHNpemU9Myxmb250ZmFjZSA9ICJib2xkIixmb3JjZT0xLGRhdGE9c3Vic2V0KGRpZmZtYXRyaXgsIAptYXhwID4gcXVhbnRpbGUoZGlmZm1hdHJpeCRtYXhwLDAuOTgpICN8IAojIGF2Z19sb2dGQyA+IDAgfAojIGF2Z19sb2dGQyA8IC0wfAojIGF2Z19sb2dGQy4xID4gMCB8CiMgYXZnX2xvZ0ZDLjEgPCAtMCkKKSxsYWJlbD1yb3cubmFtZXMoc3Vic2V0KGRpZmZtYXRyaXgsIAptYXhwID4gcXVhbnRpbGUoZGlmZm1hdHJpeCRtYXhwLDAuOTgpICN8IAojIGF2Z19sb2dGQyA+IDAgfAojIGF2Z19sb2dGQyA8IC0wIHwKIyBhdmdfbG9nRkMuMSA+IDAgfAopIyBhdmdfbG9nRkMuMSA8IC0wKQopKSt4bGFiKCJJUyB2cyBXRCIpICsgeWxhYigiT3RoZXIgdnMgQ29udHJvbCIpICt0aGVtZSgKICAjIGdldCByaWQgb2YgcGFuZWwgZ3JpZHMKICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLAogICNwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9saW5lKGNvbG9yPSJkYXJrZ3JleSIsc2l6ZT0wLjEpLAogIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgI3BhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2xpbmUoY29sb3I9ImRhcmtncmV5IixzaXplPTAuMDUpLAogICMgQ2hhbmdlIHBsb3QgYW5kIHBhbmVsIGJhY2tncm91bmQKICBwbG90LmJhY2tncm91bmQ9ZWxlbWVudF9yZWN0KGZpbGwgPSAid2hpdGUiKSwKICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAnYmxhY2snKSwKICAjIENoYW5nZSBsZWdlbmQgCiAgbGVnZW5kLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJ3aGl0ZSIsIGNvbG9yID0gTkEpLAogIGxlZ2VuZC5rZXkgPSBlbGVtZW50X3JlY3QoY29sb3IgPSAiZ3JheSIsIGZpbGwgPSAid2hpdGUiKSwKICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoY29sb3IgPSAiQmxhY2siKSwKICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICJibGFjayIpCiAgKQojbWFnbWEsaW5mZXJubywgcGxhc21hLHZpcmlkaXMKI3NjYWxlX2NvbG91cl9ncmFkaWVudChsb3cgPSAiZGFya2dyZWVuIiwgaGlnaCA9ICJyZWQiKQojRG8gcmVhY3RvbWUgYW5hbHlzaXMgYXQgdGhlIGJvdHRvbSBvZiBzY3JpcHQKaT0xCmo9MQojZm9yKGkgaW4gMTpsZW5ndGgoUmVhY3RvbWVUZXJtcykpewpmb3IoaSBpbiAxOjQpewpwd3lkYXRhIDwtIGFzLmRhdGEuZnJhbWUoUmVhY3RvbWVUZXJtc1tbaV1dKQpnZW5lc2V0IDwtIHN0cnNwbGl0KHB3eWRhdGEkZ2VuZUlELCAiLyIpCkZDbWVhbnMgPC0gZGF0YS5mcmFtZSgpCmZvcihqIGluIDE6bGVuZ3RoKGdlbmVzZXQpKXsKIGdlbmVzZXQyRkMgPC0gd2hpY2gocm93Lm5hbWVzKGRpZmZtYXRyaXgpICVpbiUgZ2VuZXNldFtbal1dKQogRkMgPC0gbWVhbihkaWZmbWF0cml4JGF2Z19sb2dGQ1tnZW5lc2V0MkZDXSxuYS5ybT1UKQogRkN2YXIgPC0gdmFyKGRpZmZtYXRyaXgkYXZnX2xvZ0ZDW2dlbmVzZXQyRkNdLG5hLnJtPVQpCiBGQy4xIDwtIG1lYW4oZGlmZm1hdHJpeCRhdmdfbG9nRkMuMVtnZW5lc2V0MkZDXSxuYS5ybT1UKQogRkMuMXZhciA8LSB2YXIoZGlmZm1hdHJpeCRhdmdfbG9nRkMuMVtnZW5lc2V0MkZDXSxuYS5ybT1UKQpGQ21lYW5zIDwtIHJiaW5kKEZDbWVhbnMsY2JpbmQoRkMsRkMuMSxGQ3ZhcixGQy4xdmFyKSkKIHByaW50KGopCn0KUmVhY3RvbWVUZXJtc1tbaV1dIDwtIGNiaW5kKFJlYWN0b21lVGVybXNbW2ldXSxGQ21lYW5zKQpwcmludChpKQp9CnBhdGhtYXRyaXggPC0gcmJpbmQoYXMuZGF0YS5mcmFtZShSZWFjdG9tZVRlcm1zW1sxXV0pLGFzLmRhdGEuZnJhbWUoUmVhY3RvbWVUZXJtc1tbMl1dKSxhcy5kYXRhLmZyYW1lKFJlYWN0b21lVGVybXNbWzNdXSksYXMuZGF0YS5mcmFtZShSZWFjdG9tZVRlcm1zW1s0XV0pKQojcGF0aG1hdHJpeCA8LSByYmluZChhcy5kYXRhLmZyYW1lKFJlYWN0b21lVGVybXNbWzFdXSksYXMuZGF0YS5mcmFtZShSZWFjdG9tZVRlcm1zW1syXV0pKQojcGF0aG1hdHJpeCA8LSByYmluZChhcy5kYXRhLmZyYW1lKFJlYWN0b21lVGVybXNbWzNdXSksYXMuZGF0YS5mcmFtZShSZWFjdG9tZVRlcm1zW1s0XV0pKQoKcGF0aG1hdHJpeCRwLmFkanVzdF9vcmlnaW5hbCA8LSBwYXRobWF0cml4JHAuYWRqdXN0CnBhdGhtYXRyaXgkcC5hZGp1c3QgPC0gLWxvZzEwKHBhdGhtYXRyaXgkcC5hZGp1c3QgKQpwYXRobWF0cml4JG1heEZDIDwtIHN1bShhYnMocGF0aG1hdHJpeCRGQyksYWJzKHBhdGhtYXRyaXgkRkMuMSkpCnBhdGhtYXRyaXggPC0gc3Vic2V0KHBhdGhtYXRyaXgsIHBhdGhtYXRyaXgkQ291bnQgPiAxKQpwYXRobWF0cml4JEFkalNlbGVjdCA8LSBwYXRobWF0cml4JHAuYWRqdXN0Kig1MDAqKDAuMithYnMocGF0aG1hdHJpeCRGQykpKQoKI3NjYWxlX2NvbG91cl9ncmFkaWVudChsb3cgPSAieWVsbG93IiwgaGlnaCA9ICJyZWQiKSArCiNzY2FsZV9jb2xvdXJfdmlyaWRpc19jKGRpcmVjdGlvbiA9IC0xKQojc2NhbGVfY29sb3VyX2dyYWRpZW50KGxvdyA9ICJibGFjayIsIGhpZ2ggPSAicmVkIikKZ2dwbG90KHBhdGhtYXRyaXgsYWVzKEZDLHk9RkMuMSxjb2xvdXI9cC5hZGp1c3Rfb3JpZ2luYWwpLGxhYmVsPXBhdGhtYXRyaXgkRGVzY3JpcHRpb24pKyBnZW9tX3BvaW50KHNpemU9cGF0aG1hdHJpeCRDb3VudCxhbHBoYT0wLjUpICtzY2FsZV9jb2xvdXJfdmlyaWRpc19jKGRpcmVjdGlvbiA9ICsxLG9wdGlvbiA9ICJ2aXJpZGlzIikgKwogIGdlb21faGxpbmUoeWludGVyY2VwdD0gMCxsaW5ldHlwZT0ic29saWQiLHNpemU9MC41LGNvbG9yPSJibGFjayIsYWxwaGE9MC41KSsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9IDAuMjUsbGluZXR5cGU9InNvbGlkIixzaXplPTAuMixjb2xvcj0iYmxhY2siLGFscGhhPTAuNSkrCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PSAtMC4yNSxsaW5ldHlwZT0ic29saWQiLHNpemU9MC4yLGNvbG9yPSJibGFjayIsYWxwaGE9MC41KSsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9IDAsbGluZXR5cGU9InNvbGlkIixzaXplPTAuNSxjb2xvcj0iYmxhY2siLGFscGhhPTAuNSkrCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0PSAwLjI1LGxpbmV0eXBlPSJzb2xpZCIsc2l6ZT0wLjIsY29sb3I9ImJsYWNrIixhbHBoYT0wLjUpKwogIGdlb21fdmxpbmUoeGludGVyY2VwdD0gLTAuMjUsbGluZXR5cGU9InNvbGlkIixzaXplPTAuMixjb2xvcj0iYmxhY2siLGFscGhhPTAuNSkrCiAgZ2VvbV90ZXh0X3JlcGVsKHNpemU9Mixmb250ZmFjZT0iYm9sZCIsZm9yY2U9MjAsZGF0YT0Kc3Vic2V0KHBhdGhtYXRyaXgsIAphYnMocGF0aG1hdHJpeCRBZGpTZWxlY3QpID4gcXVhbnRpbGUoCmFicyhwYXRobWF0cml4JEFkalNlbGVjdCksMSxuYS5ybT1UKSB8IGFicyhwYXRobWF0cml4JHAuYWRqdXN0KSA+IHF1YW50aWxlKAphYnMocGF0aG1hdHJpeCRwLmFkanVzdCksMC43NSxuYS5ybT1UKSB8CiAgYWJzKHBhdGhtYXRyaXgkRkMuMSkgPiBxdWFudGlsZShhYnMocGF0aG1hdHJpeCRGQy4xKSwxLG5hLnJtPVQpKSwKbGFiZWw9c3Vic2V0KHBhdGhtYXRyaXgsIAphYnMocGF0aG1hdHJpeCRBZGpTZWxlY3QpID4gcXVhbnRpbGUoYWJzKHBhdGhtYXRyaXgkQWRqU2VsZWN0KSwxLG5hLnJtPVQpIHwgIAogIGFicyhwYXRobWF0cml4JHAuYWRqdXN0KSA+IHF1YW50aWxlKGFicyhwYXRobWF0cml4JHAuYWRqdXN0KSwwLjc1LG5hLnJtPVQpIHwKICBhYnMocGF0aG1hdHJpeCRGQy4xKSA+IHF1YW50aWxlKGFicyhwYXRobWF0cml4JEZDLjEpLDEsbmEucm09VCkpJERlc2NyaXB0aW9uLGJveC5wYWRkaW5nID0gMC41KSt4bGFiKCJJUyB2cyBXRCIpICsgeWxhYigiT3RoZXIgdnMgQ29udHJvbCIpIApgYGAKYGBge3IgZmlnLndpZHRoPTR9CnBhdGhtYXRyaXhzb3J0IDwtIHBhdGhtYXRyaXhbb3JkZXIocGF0aG1hdHJpeCRGQyxkZWNyZWFzaW5nPVQpLF0KcGF0aG1hdHJpeHNvcnQkRGVzY3JpcHRpb24gPC0gZmFjdG9yKHBhdGhtYXRyaXhzb3J0JERlc2NyaXB0aW9uLCBsZXZlbHMgPSB1bmlxdWUocGF0aG1hdHJpeHNvcnQkRGVzY3JpcHRpb24pKSAKZ2dwbG90KHBhdGhtYXRyaXhzb3J0LCBhZXMoeD1GQywgeT1EZXNjcmlwdGlvbikpICsKICAgICAgICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IHAuYWRqdXN0X29yaWdpbmFsKSkKCnBhdGhtYXRyaXhzb3J0IDwtIHBhdGhtYXRyaXhbb3JkZXIocGF0aG1hdHJpeCRGQy4xLGRlY3JlYXNpbmc9VCksXQpwYXRobWF0cml4c29ydCREZXNjcmlwdGlvbiA8LSBmYWN0b3IocGF0aG1hdHJpeHNvcnQkRGVzY3JpcHRpb24sIGxldmVscyA9IHVuaXF1ZShwYXRobWF0cml4c29ydCREZXNjcmlwdGlvbikpIApnZ3Bsb3QocGF0aG1hdHJpeHNvcnQsIGFlcyh4PUZDLjEsIHk9RGVzY3JpcHRpb24pKSArCiAgICAgICAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBwLmFkanVzdF9vcmlnaW5hbCkpCgpwYXRobWF0cml4c29ydCA8LSBwYXRobWF0cml4W29yZGVyKHBhdGhtYXRyaXgkRkMsZGVjcmVhc2luZz1GKSxdCnBhdGhtYXRyaXhzb3J0JERlc2NyaXB0aW9uIDwtIGZhY3RvcihwYXRobWF0cml4c29ydCREZXNjcmlwdGlvbiwgbGV2ZWxzID0gdW5pcXVlKHBhdGhtYXRyaXhzb3J0JERlc2NyaXB0aW9uKSkgCnBhdGhtYXRyaXhzb3J0IDwtIHBhdGhtYXRyaXhzb3J0WyFwYXRobWF0cml4c29ydCRwLmFkanVzdF9vcmlnaW5hbCA+IDAuMDEsXQpsaWJyYXJ5KHJlc2hhcGUyKQpwYXRobWF0cml4c29ydElTV0QgPC0gcGF0aG1hdHJpeHNvcnRbLGMoMiwxMCwxNCldCnBhdGhtYXRyaXhzb3J0SVNXRCRHcm91cCA8LSByZXAoIklTdnNXRCIsbnJvdyhwYXRobWF0cml4c29ydElTV0QpKQpwYXRobWF0cml4c29ydENudHJsSVNXRCA8LSBwYXRobWF0cml4c29ydFssYygyLDExLDE0KV0KY29sbmFtZXMocGF0aG1hdHJpeHNvcnRDbnRybElTV0QpWzJdIDwtICJGQyIKcGF0aG1hdHJpeHNvcnRDbnRybElTV0QkR3JvdXAgPC0gcmVwKCJDb250cm9sdnNJUy1XRCIsbnJvdyhwYXRobWF0cml4c29ydENudHJsSVNXRCkpCnBhdGhtYXRyaXhzb3J0IDwtIHJiaW5kKHBhdGhtYXRyaXhzb3J0SVNXRCxwYXRobWF0cml4c29ydENudHJsSVNXRCkKZ2dwbG90KHBhdGhtYXRyaXhzb3J0LCBhZXMoeD1GQywgeT1EZXNjcmlwdGlvbikpICsKICAgICAgICBnZW9tX2xpbmUoYWVzKGdyb3VwID0gRGVzY3JpcHRpb24pKSArCiAgICAgICAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBHcm91cCkpCgpwYXRobWF0cml4c29ydCA8LSBwYXRobWF0cml4W29yZGVyKHBhdGhtYXRyaXgkRkMsZGVjcmVhc2luZz1GKSxdCnBhdGhtYXRyaXhzb3J0JERlc2NyaXB0aW9uIDwtIGZhY3RvcihwYXRobWF0cml4c29ydCREZXNjcmlwdGlvbiwgbGV2ZWxzID0gdW5pcXVlKHBhdGhtYXRyaXhzb3J0JERlc2NyaXB0aW9uKSkgCnBhdGhtYXRyaXhzb3J0IDwtIHBhdGhtYXRyaXhzb3J0WyFwYXRobWF0cml4c29ydCRwLmFkanVzdF9vcmlnaW5hbCA+IDAuMDEsXQpsaWJyYXJ5KHJlc2hhcGUyKQpnZ3Bsb3QocGF0aG1hdHJpeHNvcnQsIGFlcyh4PUZDLCB5PURlc2NyaXB0aW9uKSkgKwogICAgICAgIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gQ291bnQpKQoKcGF0aG1hdHJpeHNvcnQgPC0gcGF0aG1hdHJpeFtvcmRlcihwYXRobWF0cml4JEZDLjEsZGVjcmVhc2luZz1GKSxdCnBhdGhtYXRyaXhzb3J0JERlc2NyaXB0aW9uIDwtIGZhY3RvcihwYXRobWF0cml4c29ydCREZXNjcmlwdGlvbiwgbGV2ZWxzID0gdW5pcXVlKHBhdGhtYXRyaXhzb3J0JERlc2NyaXB0aW9uKSkgCnBhdGhtYXRyaXhzb3J0IDwtIHBhdGhtYXRyaXhzb3J0WyFwYXRobWF0cml4c29ydCRwLmFkanVzdF9vcmlnaW5hbCA+IDAuMDEsXQpsaWJyYXJ5KHJlc2hhcGUyKQpnZ3Bsb3QocGF0aG1hdHJpeHNvcnQsIGFlcyh4PUZDLjEsIHk9RGVzY3JpcHRpb24pKSArCiAgICAgICAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBDb3VudCkpCmBgYApgYGB7cn0KI0NvZXhwcmVzc2lvbiBNT0w1IGFuZCBNT0w2IHZzIE1PTDEKCm9saWdvcy5pbnRlZ3JhdGVkT0wyNTYgPC0gc3Vic2V0KG9saWdvcy5pbnRlZ3JhdGVkLHByZWRpY3RlZC5pZCAlaW4lIGMoIk1PTDIiLCJNT0w1IiwiTU9MNiIpKQoKUHRnZHNleHByZXNzaW9uIDwtIG9saWdvcy5pbnRlZ3JhdGVkT0wyNTZAYXNzYXlzJFJOQUBjb3VudHNbIlB0Z2RzIixdCktsazZleHByZXNzaW9uIDwtIG9saWdvcy5pbnRlZ3JhdGVkT0wyNTZAYXNzYXlzJFJOQUBjb3VudHNbIktsazYiLF0KCgpFeHByZXNzaW9uQ29tYm8gPC0gYXMuZGF0YS5mcmFtZSh0KG9saWdvcy5pbnRlZ3JhdGVkT0wyNTZAYXNzYXlzJFJOQUBjb3VudHNbYygiUHRnZHMiLCJLbGs2IiksXSkpCiNwbG90KHg9bG9nKEV4cHJlc3Npb25Db21ibyRQdGdkcysxKSx5PWxvZyhFeHByZXNzaW9uQ29tYm8kS2xrNisxKSkKRXhwcmVzc2lvbkNvbWJvc29ydGVkIDwtIEV4cHJlc3Npb25Db21ib1tvcmRlcihFeHByZXNzaW9uQ29tYm8kUHRnZHMsZGVjcmVhc2luZyA9IFRSVUUpLF0KYmFycGxvdChFeHByZXNzaW9uQ29tYm9zb3J0ZWQkUHRnZHMpCmJhcnBsb3QoRXhwcmVzc2lvbkNvbWJvc29ydGVkJEtsazYpCgpFeHByZXNzaW9uQ29tYm9zb3J0ZWQgPC0gRXhwcmVzc2lvbkNvbWJvW29yZGVyKEV4cHJlc3Npb25Db21ibyRLbGs2LGRlY3JlYXNpbmcgPSBGQUxTRSksXQpiYXJwbG90KEV4cHJlc3Npb25Db21ib3NvcnRlZCRQdGdkcykKYmFycGxvdChFeHByZXNzaW9uQ29tYm9zb3J0ZWQkS2xrNikKCm5hbWVzTU9MNTYgPC0gb2xpZ29zLmludGVncmF0ZWRPTDI1NkBtZXRhLmRhdGEkcHJlZGljdGVkLmlkIApwbG90KHg9RXhwcmVzc2lvbkNvbWJvJFB0Z2RzLHk9b2xpZ29zLmludGVncmF0ZWRPTDI1NkBtZXRhLmRhdGEkcHJlZGljdGlvbi5zY29yZS5NT0w1KQpwbG90KHg9RXhwcmVzc2lvbkNvbWJvJFB0Z2RzLHk9b2xpZ29zLmludGVncmF0ZWRPTDI1NkBtZXRhLmRhdGEkcHJlZGljdGlvbi5zY29yZS5NT0w2KQoKRXhwcmVzc2lvbkNvbWJvc29ydGVkIDwtIEV4cHJlc3Npb25Db21ib1tvcmRlcihFeHByZXNzaW9uQ29tYm8kUHRnZHMsZGVjcmVhc2luZyA9IFRSVUUpLF0KTU9MNnByZWQgPC0gb2xpZ29zLmludGVncmF0ZWRPTDI1NkBtZXRhLmRhdGEkcHJlZGljdGlvbi5zY29yZS5NT0w2Cm5hbWVzKE1PTDZwcmVkKSA8LSByb3cubmFtZXMob2xpZ29zLmludGVncmF0ZWRPTDI1NkBtZXRhLmRhdGEpCmJhcnBsb3QoTU9MNnByZWRbcm93Lm5hbWVzKEV4cHJlc3Npb25Db21ib3NvcnRlZCldKQoKTU9MNXByZWQgPC0gb2xpZ29zLmludGVncmF0ZWRPTDI1NkBtZXRhLmRhdGEkcHJlZGljdGlvbi5zY29yZS5NT0w1Cm5hbWVzKE1PTDVwcmVkKSA8LSByb3cubmFtZXMob2xpZ29zLmludGVncmF0ZWRPTDI1NkBtZXRhLmRhdGEpCmJhcnBsb3QoTU9MNXByZWRbcm93Lm5hbWVzKEV4cHJlc3Npb25Db21ib3NvcnRlZCldKQoKTU9MNTZwcmVkIDwtIHJvd01lYW5zKGNiaW5kKE1PTDVwcmVkLE1PTDZwcmVkKSkKYmFycGxvdChNT0w1NnByZWRbcm93Lm5hbWVzKEV4cHJlc3Npb25Db21ib3NvcnRlZCldKQpwbG90KE1PTDU2cHJlZCxFeHByZXNzaW9uQ29tYm9zb3J0ZWQkUHRnZHMpCgpFeHByZXNzaW9uQ29tYm9zb3J0ZWQgPC0gRXhwcmVzc2lvbkNvbWJvW29yZGVyKEV4cHJlc3Npb25Db21ibyRLbGs2LGRlY3JlYXNpbmcgPSBGQUxTRSksXQpNT0wycHJlZCA8LSBvbGlnb3MuaW50ZWdyYXRlZE9MMjU2QG1ldGEuZGF0YSRwcmVkaWN0aW9uLnNjb3JlLk1PTDIKbmFtZXMoTU9MMnByZWQpIDwtIHJvdy5uYW1lcyhvbGlnb3MuaW50ZWdyYXRlZE9MMjU2QG1ldGEuZGF0YSkKYmFycGxvdChNT0wycHJlZFtyb3cubmFtZXMoRXhwcmVzc2lvbkNvbWJvc29ydGVkKV0pCgpFeHByZXNzaW9uQ29tYm8gPC0gYXMuZGF0YS5mcmFtZSh0KG9saWdvcy5pbnRlZ3JhdGVkT0wyNTZAYXNzYXlzJFJOQUBjb3VudHNbYygiUHRnZHMiLCJLbGs2IiksXSkpCnByZWQgPC0gKE1PTDU2cHJlZCktKE1PTDJwcmVkKQpFeHByZXNzaW9uQ29tYm9zb3J0ZWQgPC0gRXhwcmVzc2lvbkNvbWJvW29yZGVyKEV4cHJlc3Npb25Db21ibyRQdGdkcyxkZWNyZWFzaW5nID0gVFJVRSksXQpiYXJwbG90KEV4cHJlc3Npb25Db21ib3NvcnRlZCRQdGdkcykKYmFycGxvdChwcmVkW3Jvdy5uYW1lcyhFeHByZXNzaW9uQ29tYm9zb3J0ZWQpXSkKCkV4cHJlc3Npb25Db21ib3NvcnRlZCA8LSBFeHByZXNzaW9uQ29tYm9bb3JkZXIoRXhwcmVzc2lvbkNvbWJvJEtsazYsZGVjcmVhc2luZyA9IEZBTFNFKSxdCmJhcnBsb3QoRXhwcmVzc2lvbkNvbWJvc29ydGVkJEtsazYpCmJhcnBsb3QocHJlZFtyb3cubmFtZXMoRXhwcmVzc2lvbkNvbWJvc29ydGVkKV0pCgpFeHByZXNzaW9uQ29tYm8gPC0gYXMuZGF0YS5mcmFtZSh0KG9saWdvcy5pbnRlZ3JhdGVkT0wyNTZAYXNzYXlzJFJOQUBjb3VudHNbYygiUHRnZHMiLCJLbGs2IiksXSkpCk1PTDU2SUQgPC0gIDEqb2xpZ29zLmludGVncmF0ZWRPTDI1NkBtZXRhLmRhdGEkcHJlZGljdGVkLmlkICVpbiUgYygiTU9MNSIsIk1PTDYiKQpNT0wySUQgPC0gIC0xKm9saWdvcy5pbnRlZ3JhdGVkT0wyNTZAbWV0YS5kYXRhJHByZWRpY3RlZC5pZCAlaW4lIGMoIk1PTDIiKQpNT0xJRCA8LSBNT0w1NklEK01PTDJJRApuYW1lcyhNT0xJRCkgPC0gcm93Lm5hbWVzKG9saWdvcy5pbnRlZ3JhdGVkT0wyNTZAbWV0YS5kYXRhKQoKRXhwcmVzc2lvbkNvbWJvc29ydGVkIDwtIEV4cHJlc3Npb25Db21ib1tvcmRlcihFeHByZXNzaW9uQ29tYm8kUHRnZHMsZGVjcmVhc2luZyA9IFRSVUUpLF0KYmFycGxvdChFeHByZXNzaW9uQ29tYm9zb3J0ZWQkUHRnZHMpCmJhcnBsb3QoTU9MSURbcm93Lm5hbWVzKEV4cHJlc3Npb25Db21ib3NvcnRlZCldKQpFeHByZXNzaW9uQ29tYm9zb3J0ZWQgPC0gRXhwcmVzc2lvbkNvbWJvW29yZGVyKEV4cHJlc3Npb25Db21ibyRLbGs2LGRlY3JlYXNpbmcgPSBUUlVFKSxdCmJhcnBsb3QoRXhwcmVzc2lvbkNvbWJvc29ydGVkJEtsazYpCmJhcnBsb3QoTU9MSURbcm93Lm5hbWVzKEV4cHJlc3Npb25Db21ib3NvcnRlZCldKQoKCgpFeHByZXNzaW9uQ29tYm8gPC0gYXMuZGF0YS5mcmFtZSh0KG9saWdvcy5pbnRlZ3JhdGVkT0wyNTZAYXNzYXlzJFJOQUBjb3VudHNbYygiUHRnZHMiLCJLbGs2IiksXSkpCk1PTElEIDwtIGRyb3BsZXZlbHMob2xpZ29zLmludGVncmF0ZWRPTDI1NkBtZXRhLmRhdGEkcHJlZGljdGVkLmlkKQpuYW1lcyhNT0xJRCkgPC0gcm93Lm5hbWVzKG9saWdvcy5pbnRlZ3JhdGVkT0wyNTZAbWV0YS5kYXRhKQpFeHByZXNzaW9uQ29tYm9zb3J0ZWQgPC0gRXhwcmVzc2lvbkNvbWJvW29yZGVyKEV4cHJlc3Npb25Db21ibyRQdGdkcyxkZWNyZWFzaW5nID0gVFJVRSksXQpFeHByZXNzaW9uQ29tYm9zb3J0ZWQkSUQgPC0gTU9MSURbcm93Lm5hbWVzKEV4cHJlc3Npb25Db21ib3NvcnRlZCldIApFeHByZXNzaW9uQ29tYm9zb3J0ZWQkb3JkZXIgPC0gc2VxX2xlbihucm93KEV4cHJlc3Npb25Db21ib3NvcnRlZCkpCmdncGxvdChFeHByZXNzaW9uQ29tYm9zb3J0ZWQsIGFlcyh4PW9yZGVyLCBmaWxsPUlEKSkgKwogIGdlb21fZGVuc2l0eShhbHBoYT0wLjQpCgpFeHByZXNzaW9uQ29tYm8gPC0gYXMuZGF0YS5mcmFtZSh0KG9saWdvcy5pbnRlZ3JhdGVkT0wyNTZAYXNzYXlzJFJOQUBjb3VudHNbYygiUHRnZHMiLCJLbGs2IiwiUzEwMGIiLCJPcGFsaW4iLCJIb3B4IiwiQXBvZSIsIkFwb2QiKSxdKSkKTU9MSUQgPC0gZHJvcGxldmVscyhvbGlnb3MuaW50ZWdyYXRlZE9MMjU2QG1ldGEuZGF0YSRwcmVkaWN0ZWQuaWQpCm5hbWVzKE1PTElEKSA8LSByb3cubmFtZXMob2xpZ29zLmludGVncmF0ZWRPTDI1NkBtZXRhLmRhdGEpCkV4cHJlc3Npb25Db21ib3NvcnRlZCA8LSBFeHByZXNzaW9uQ29tYm9bb3JkZXIoRXhwcmVzc2lvbkNvbWJvJEtsazYsZGVjcmVhc2luZyA9IFRSVUUpLF0KRXhwcmVzc2lvbkNvbWJvc29ydGVkJElEIDwtIE1PTElEW3Jvdy5uYW1lcyhFeHByZXNzaW9uQ29tYm9zb3J0ZWQpXSAKRXhwcmVzc2lvbkNvbWJvc29ydGVkJG9yZGVyIDwtIHNlcV9sZW4obnJvdyhFeHByZXNzaW9uQ29tYm9zb3J0ZWQpKQpnZ3Bsb3QoRXhwcmVzc2lvbkNvbWJvc29ydGVkLCBhZXMoeD1vcmRlciwgZmlsbD1JRCkpICsKICBnZW9tX2RlbnNpdHkoYWxwaGE9MC40KQoKZ2dwbG90KEV4cHJlc3Npb25Db21ib3NvcnRlZCwgYWVzKHg9bG9nKFB0Z2RzKzEpLCBmaWxsPUlEKSkgKwogIGdlb21fZGVuc2l0eShhbHBoYT0wLjQpCgpnZ3Bsb3QoRXhwcmVzc2lvbkNvbWJvc29ydGVkLCBhZXMoeD1sb2coS2xrNisxKSwgZmlsbD1JRCkpICsKICBnZW9tX2RlbnNpdHkoYWxwaGE9MC40KQoKZ2dwbG90KEV4cHJlc3Npb25Db21ib3NvcnRlZCwgYWVzKHg9bG9nKFMxMDBiKzEpLCBmaWxsPUlEKSkgKwogIGdlb21fZGVuc2l0eShhbHBoYT0wLjQpCmdncGxvdChFeHByZXNzaW9uQ29tYm9zb3J0ZWQsIGFlcyh4PWxvZyhPcGFsaW4rMSksIGZpbGw9SUQpKSArCiAgZ2VvbV9kZW5zaXR5KGFscGhhPTAuNCkKZ2dwbG90KEV4cHJlc3Npb25Db21ib3NvcnRlZCwgYWVzKHg9bG9nKEhvcHgrMSksIGZpbGw9SUQpKSArCiAgZ2VvbV9kZW5zaXR5KGFscGhhPTAuNCkKZ2dwbG90KEV4cHJlc3Npb25Db21ib3NvcnRlZCwgYWVzKHg9bG9nKEFwb2UrMSksIGZpbGw9SUQpKSArCiAgZ2VvbV9kZW5zaXR5KGFscGhhPTAuNCkKZ2dwbG90KEV4cHJlc3Npb25Db21ib3NvcnRlZCwgYWVzKHg9bG9nKEFwb2QrMSksIGZpbGw9SUQpKSArCiAgZ2VvbV9kZW5zaXR5KGFscGhhPTAuNCkKCmdncGxvdChFeHByZXNzaW9uQ29tYm9zb3J0ZWQsIGFlcyh4PWxvZyhLbGs2KzEpLHk9bG9nKFB0Z2RzKzEpLCBjb2xvcj1JRCkpICsKICBnZW9tX3BvaW50KGFscGhhPTAuNCkKCmdncGxvdChFeHByZXNzaW9uQ29tYm9zb3J0ZWQsIGFlcyh4PVB0Z2RzLCBmaWxsPUlEKSkgKwogIGdlb21fZGVuc2l0eShhbHBoYT0wLjQpCgpnZ3Bsb3QoRXhwcmVzc2lvbkNvbWJvc29ydGVkLCBhZXMoeD1LbGs2LCBmaWxsPUlEKSkgKwogIGdlb21fZGVuc2l0eShhbHBoYT0wLjQpCgoKYGBg